Parse from single node in multiple nodes that have same name - c#

<physicalResource>
<prName>PRS_EID</prName>
<prDescription>PRS_EID</prDescription>
<physicalResourceCharacteristic>
<characteristic>
<name>eidno</name>
<value>SH001499000</value>
</characteristic>
<characteristic>
<name>flatno</name>
<value>14303</value>
</characteristic>
</physicalResourceCharacteristic>
</physicalResource>
<physicalResource>
<prName>PRS_OLT</prName>
<prDescription>PRS_OLT</prDescription>
<physicalResourceCharacteristic>
<characteristic>
<name>device</name>
<value>WC-OMU-AO01</value>
</characteristic>
<characteristic>
<name>frame</name>
<value>1</value>
</characteristic>
<characteristic>
<name>port</name>
<value>5</value>
</characteristic>
</physicalResourceCharacteristic>
</physicalResource>
Hello Dears.. I have an xml file. It contains different nodes with same node name. In the example under physicalResource node, I want to extract prName and all characteristic's name and value. But I cant parse them seperately.
I'm using
nodeListPrs = root.SelectNodes("/physicalResource/physicalResourceCharacteristic/characteristic", nsmgr);
It extracts all charactics value for both nodes. How can i extract these parameters from single physicalResource node?

You can use xmldocument and load that xml to xmldocument then you can use selectsinglenode. It would help!!
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
xdoc.DocumentElement.SelectSingleNode(path for the node..);

Use XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication78
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("physicalResource").Select(x => new {
prName = (string)x.Element("prName"),
characteristics = x.Descendants("characteristic").Select(y => new {
name = (string)y.Element("name"),
value = (string)y.Element("value")
}).ToList()
}).ToList();
}
}
}

To select all prName nodes with the corresponding characteristic nodes of the first physicalResourceCharacteristic node, you would use the follwing XPath expression in SelectNodes.
/res/physicalResource[1]/physicalResourceCharacteristic/characteristic | /res/physicalResource[1]/prName
The result is
<?xml version="1.0" encoding="UTF-8"?>
<prName>PRS_EID</prName>
<characteristic>
<name>eidno</name>
<value>SH001499000</value>
</characteristic>
<characteristic>
<name>flatno</name>
<value>14303</value>
</characteristic>
I'm not sure if this is what you strive for. You could iterate over the count of physicalResource constructing [xxx] XPath-expressions to get a nodeset with a couple of these entries. Or you could omit the [1] to get a nodeset with all physicalResources prefixed by prNames, but with mixed node-types.

Thanks all. I have resolved by using SelectSingleNode and SelectNodes cascade:
XmlNodeList nodeListPrs = root.SelectNodes("/ns2:serviceOrder/resourceFacingService/physicalResource", nsmgr);
foreach (XmlNode book in nodeListPrs)
{
string prsName = book["prName"].InnerText;
try
{
nodeListPrsCh = book.SelectSingleNode("physicalResourceCharacteristic").SelectNodes("characteristic");
foreach (XmlNode characteristics in nodeListPrsCh)
{
dataGridView3.Rows.Add();
dataGridView3.Rows[i].Cells[0].Value = prsName;
try { string name = characteristics["name"].InnerText; dataGridView3.Rows[i].Cells[1].Value = name; }
catch { dataGridView3.Rows[i].Cells[1].Value = "-"; }
try { string value = characteristics["value"].InnerText; dataGridView3.Rows[i].Cells[2].Value = value; }
catch { dataGridView3.Rows[i].Cells[2].Value = "-"; }
i = i + 1;
}
}
catch
{
dataGridView3.Rows.Add();
dataGridView3.Rows[i].Cells[0].Value = prsName;
dataGridView3.Rows[i].Cells[1].Value = "-";
dataGridView3.Rows[i].Cells[2].Value = "-";
i = i + 1;
}
}

Related

Retrieve list of xml tag of child nodes in linQ to xml

I'm trying to get all the list of the different child nodes (not starting from root) of a loaded XML into a list of strings, I had done using System.Xml library but I want to write the same code with LINQ to XML too.
I had found a code that helped me a lot but it starts from Root, here is the code:
List<string> nodesNames = new List<string>();
XDocument xdoc1 = XDocument.Load(destinationPath);
XElement root = xdoc1.Document.Root;
foreach (var name in root.DescendantNodes().OfType<XElement>()
.Select(x => x.Name).Distinct())
{
if (!nodesNames.Contains(name.ToString()))
nodesNames.Add(name.ToString());
}
With this, I get the list of all child nodes + the parent node too, which I don't want to use.FirstChild or to delete manually from the list because I want a TOTALLY dynamic code and I have in input the parent node passed by the user.
For a better comprehension, this is the code that working for me but with System.Xml:
List<string> nodesNames = new List<string>();
XmlDocument doc = new XmlDocument();
doc.Load(destinationPath);
XmlNodeList elemList = doc.GetElementsByTagName(inputParentNode);
for (int i = 0; i < elemList.Count; i++)
{
XmlNodeList cnList = (elemList[i].ChildNodes);
for (int j = 0; j < cnList.Count; j++)
{
string name = cnList[j].Name;
if (!nodesNames.Contains(name))
nodesNames.Add(name);
}
}
And this is an easy sample of the XML:
<?xml version='1.0' encoding='UTF-8'?>
<parentlist>
<parent>
<firstchild>someValue</firstchild>
<secondchild>someValue</secondchild>
</parent>
<parent>
<firstchild>someValue</firstchild>
<secondchild>someValue</secondchild>
<thirdchild>someValue</thirdchild>
</parent>
</parentlist>
To resume:
in the first case i obtain nodesNames = ["parent", "firstchild", "secondchild", "thirdchild"]
in the second case i obtain nodesNames = ["firstchild", "secondchild", "thirdchild"]
I just want to fix the first to obtain the same result as the second.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement parentlist= doc.Root;
List<string> descendents = parentlist.Descendants().Where(x => x.HasElements).Select(x => string.Join(",", x.Name.LocalName, string.Join(",", x.Elements().Select(y => y.Name.LocalName)))).ToList();
}
}
}

How to find attributes and their values in an XML without the name?

I'm new to C#
Here the xml:
<ROOT>
<Columns BaseXPath="//Orders/Position/">
<Colum XPath="#PositionSK" Name="Position"/>
<Colum XPath="#PosGroup" Name="Gruppen-Nr"/>
<Colum XPath="#PosNumber" Name="PositionsNr"/>
<Colum XPath="#PositionCommercialTypeSK" Name="Status"/>
<Colum XPath="#BundlePositionSK" Name="BundlePositionSK"/>
<Colum XPath="#MainPositionSK" Name="MainPositionSK"/>
<Colum XPath="#SalesAgentPrice" Name="Preis"/>
<Colum XPath="#BookingUnitSK" Name="Buch"/>
<Colum XPath="#ContentComponentCommSK" Name="IKO"/>
<Colum XPath="#PositionTypeSK" Name="PositionsTyp"/>
<Colum XPath="//Advertisement[#AdvertisementSK = PositionAdvertisementRelationship/#AdvertisementSK]/#AdvertisementSK" Name="AdvertisementSK"/>
<Colum XPath="//Advertisement[#AdvertisementSK = PositionAdvertisementRelationship/#AdvertisementSK]/#AdvertisementTypeSK" Name="Formatvorgabe"/>
</Columns>
</ROOT>
This xml can always change. So its never the same. Sometimes there are more infos, sometimes less.
This xml give me the certain info, which should be searched in the second "main xml".
So now I know that I have to find the Attribute of PositionSK, PosGroup, PositionCommercialTypeSK, ... . In the other xml.
But how can I do this? The name is never the same, so I need a placeholder for them?
I tried this:
public class ResultNames
{
XmlDocument xml = new XmlDocument();
public List<ResultNames> GetRightNames (string file)
{
xml.Load(file); //this is the xml file
var list = xml.SelectNodes("//ROOT/Columns/Colum");
foreach ( XmlNode colum in list)
{
XmlNode bla = colum.Attributes; //I dont know what I can do here, without any name.
}
return null;
}
and what is with the other xml file, do I need an extra class?
A small sample from the other xml:
<Set>
<Orders OrderSK="0013233309" OrderTypeSK="ORDER" OrderDate="2000-01-01T12:00:00" OrderPrice="0.0000" OrderQuantity="0.00" DistrictSK="0026070180" PaymentTypeSK="E" OrderCreationTypeSK="SNW5ORD" SalesAgentSK="0020025518" ChangeDate="2018-01-25T15:48:29" SalesOrganisationSK="K10-100-1000-50-65" ChangeDateFS="2017-12-11T15:25:21" Source="CORE" Status="C">
<Position PosNumber="3" PosGroup="5" PositionTypeSK="ONL" PositionCommercialTypeSK="DEFAULT"
But its a lot bigger.
Use Xml Linq along with a dictionary
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME1 = #"c:\temp\test.xml";
const string FILENAME2 = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME1);
Dictionary<string, XElement> dict = doc.Descendants("Columns").FirstOrDefault().Elements()
.GroupBy(x => (string)x.Attribute("XPath"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
XDocument order = XDocument.Load(FILENAME2);
List<XElement> positions = order.Descendants("Position").ToList();
foreach (XElement position in positions)
{
foreach (XAttribute attribute in position.Attributes())
{
string name = attribute.Name.LocalName;
string value = (string)attribute;
XElement element = dict["#" + name];
element.SetValue(value);
}
}
}
}
}
Code below just gets the Name from first Xml file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME1 = #"c:\temp\test.xml";
const string FILENAME2 = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME1);
Dictionary<string, string> dict = doc.Descendants("Columns").FirstOrDefault().Elements()
.GroupBy(x => (string)x.Attribute("XPath"), y => (string)y.Attribute("Name"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
XDocument order = XDocument.Load(FILENAME2);
List<XElement> positions = order.Descendants("Position").ToList();
foreach (XElement position in positions)
{
foreach (XAttribute attribute in position.Attributes())
{
string name = attribute.Name.LocalName;
string value = (string)attribute;
if(dict.ContainsKey("#" + name))
{
string xName = dict["#" + name];
Console.WriteLine("Key = '{0}', Name = '{1}'", name, xName);
}
else
{
Console.WriteLine("Not in dictionary : Key = '{0}'", name);
}
}
}
}
}
}
I had to make some assumptions about the data you are working with, since you haven't provided examples of everything.
The first assumption is the format of the 2nd XML document. I had to guess from the format of the first document.
The 2nd assumption is that that XPATHs specified in the 1st document Colum elements always point to an Attribute.
void Main()
{
string xml1 =
#"<ROOT>
<Columns BaseXPath=""//Orders/Position/"">
<Colum XPath=""#PositionSK"" Name=""Position""/>
<Colum XPath=""#PosGroup"" Name=""Gruppen-Nr""/>
</Columns>
</ROOT>";
string data =
#"<Set>
<Orders>
<Position PositionSK=""A"" PosGroup=""1"" SomeOtherAttribute=""ABC"" />
<Position PositionSK=""B"" PosGroup=""2"" SomeOtherAttribute=""DEF"" />
</Orders>
</Set>";
var schemaDoc = XDocument.Parse(xml1);
var dataDoc = XDocument.Parse(data);
var itemsXPath = schemaDoc.Descendants("Columns").FirstOrDefault()?.Attribute("BaseXPath").Value;
var basePath = schemaDoc.Descendants("Columns").FirstOrDefault().Attribute("BaseXPath").Value;
// XPATH isn't supposed to end with a trailing "/".
if (basePath.EndsWith("/"))
{
basePath = basePath.Substring(0, basePath.Length - 1);
}
var lines = dataDoc.XPathSelectElements(basePath);
var rowIndex = 0;
foreach (var line in lines)
{
Console.WriteLine($"---Row {rowIndex}");
foreach (var col in schemaDoc.Descendants("Colum"))
{
var columnName = col.Attribute("Name").Value;
Console.Write($"{columnName}: ");
var columnValue = ((XAttribute)((IEnumerable<Object>)line.XPathEvaluate(col.Attribute("XPath").Value)).FirstOrDefault()).Value;
Console.WriteLine(columnValue);
}
Console.WriteLine();
rowIndex++;
}
}
This produces the following output:
---Row 0
Position: A
Gruppen-Nr: 1
---Row 1
Position: B
Gruppen-Nr: 2
You can change the attributes that are output by adjusting the content of xml1.

How to get XmlElement from XmlNodeList in C#?

Below is sample of my XML file
<?xml version="1.0" encoding="UTF-8"?>
<summary>
<testresult>
<result value="10" name="long">100</result>
<result value="12" name="short">200</result>
<result value="14" name="long">300</result>
</testresult>
<testresult>
<result value="10" name="short">50</result>
<result value="12" name="short">60</result>
<result value="14" name="long">70</result>
</testresult>
</summary>
I need to get attribute values for result elements.
I done it using foreach loop as below.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(item.Value);
XmlNodeList nodelist = xmlDoc.SelectNodes("//testresult");
for (int i = 0; i < nodelist.Count; i++)
{
foreach (XmlElement child in nodelist[i])
{
if (child.HasAttributes)
{
result.Add(child.Attributes["value"].Value); //This is working fine.
}
}
}
My ultimate goal is to identify the name and get value if name = "long" only.
for that I need to get the value of name Attribute.
I need to do that without using foreach loop. Any suggestion to achieve my task inside the for loop ?
Thanks.
You could parse your xml easy with XDocument:
var xDoc = XDocument.Load(#"YourXmlFile");
var result = xDoc.Descendants("result")
.Where(x=>x.Attribute("name").Value=="long")
.Select(x=>x.Value);
If you can use LINQ to XML then you even can get results as integers
var xdoc = XDocument.Load(item.Value);
var results = from r in xdoc.Descendants("result")
where (string)r.Attribute("name") == "long"
select (int)r.Attribute("value");
Output:
[10, 14, 14]
If you have to use XmlDocument
var result = from XmlElement tr in xmlDoc.SelectNodes("//testresult")
from XmlElement r in tr
where r.Attributes["name"].Value == "long"
select r.Attributes["value"].Value;
You can also provide more preciese XPath
var result = from XmlElement r in xmlDoc.SelectNodes("//testresult/result[#name='long']")
select r.Attributes["value"].Value;
Using xml linq to get all the values
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication43
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("testresult").Select(x => new {
result = x.Elements("result").Select(y => new {
value = (int)y.Attribute("value"),
name = (string)y.Attribute("name"),
text = (int)y
}).ToList()
}).ToList();
}
}
}

insert the new child node in existing xml based on id in C#

I want to add child node in existing xml
<tblTemp>
<Details>
<LoginId>4</LoginId>
<AId>2</AId>
<OId>763</OId>
<LDate>2016-09-26</LDate>
<LTime>15:27:39</LTime>
<ReasonId>1</ReasonId>
<Flag>2</Flag>
</Details>
<Details>
<LoginId>3</LoginId>
<AId>2</AId>
<OId>763</OId>
<LDate>2016-09-26</LDate>
<LTime>12:22:39</LTime>
<ReasonId>4</ReasonId>
<Flag>2</Flag>
</Details>
<Details>
<LoginId>1</LoginId>
<AId>1</AId>
<OId>765</OId>
<LDate>2016-09-26</LDate>
<LTime>10:22:39</LTime>
<ReasonId>4</ReasonId>
<Flag>2</Flag>
</Details>
</tblTemp>
And i want output like this
<tblTemp>
<Details>
<LoginId>4</LoginId>
<AId>2</AId>
<OId>763</OId>
<LDate>2016-09-26</LDate>
<LTime>15:27:39</LTime>
<FDate>2016-09-26</FDate>
<FTime>16:50:30</FTime>
<ReasonId>1</ReasonId>
<Flag>2</Flag>
</Details>
<Details>
<LoginId>3</LoginId>
<AId>2</AId>
<OId>763</OId>
<LDate>2016-09-26</LDate>
<LTime>12:22:39</LTime>
<FDate>2016-09-26</FDate>
<FTime>13:36:30</FTime>
<ReasonId>4</ReasonId>
<Flag>2</Flag>
</Details>
<Details>
<LoginId>1</LoginId>
<AId>1</AId>
<OId>765</OId>
<LDate>2016-09-26</LDate>
<LTime>10:22:39</LTime>
<FDate>2016-09-26</FDate>
<FTime>11:53:45</FTime>
<ReasonId>4</ReasonId>
<Flag>2</Flag>
</Details>
</tblTemp>
Based on LoginId I want to add child node in xml file.I have been trying code like this.
//code for adding child node
string strDBDir = "C:\\XMLfile.xml";
try
{
DataSet dsxml = new DataSet();
DataView DvXML = null;
dsxml.ReadXml(strDBDir);
DvXML = dsxml.Tables[0].DefaultView;
DvXML.RowFilter = "AId = '" + AId + "'";
if (File.Exists(strDBDir))
{
if (DvXML.ToTable().Rows.Count > 0)
{
LoginId = Convert.ToInt32(DvXML.ToTable().Rows[0]["LoginId"]);
XmlDocument originalXml = new XmlDocument();
originalXml.Load(strDBDir);
XmlNode TechReport = originalXml.SelectSingleNode("Details");
XmlNode Data = originalXml.CreateNode(XmlNodeType.Element, "FDate", null);
TechReport.AppendChild(Data);
originalXml.Save(strDBDir);
}
}
catch
{
}
For the above code i get an exception-"Object reference not set to an instance of an object"
Can you please guide on this,how to add the FDate and Ftime in DBfile.xml based on LoginId and AId.I have been struggling for this.
You can achieve that by changing your search.
Instead of the looking only for "Details" which will add the child elements randomly, use "Details[LoginId='4']"
XmlNode TechReport = originalXml.SelectSingleNode("Details[LoginId='4']");
Is this what you are looking for?
Also, I think you will need to use CreateElement instead of CreateNode
Using xml linq and changing all occurances
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication16
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
foreach (XElement detail in doc.Descendants("Details"))
{
detail.Add(new object[] {
new XElement("LTime", DateTime.Now.ToString("hh:mm:ss")),
new XElement("FDate", DateTime.Now.Date.ToShortDateString())
});
}
}
}
}
you can try this way to adding a new child:
XmlDocument xml = new XmlDocument();
xml.Load(strDBDir);
XmlNodeList xnList = xml.SelectNodes("/tblTemp/Details");
foreach (XmlNode xn in xnList)
{
// here your **if** statement with check on loginId in **xn**
XElement child = new XElement("FDate");
child.SetValue("2016-09-26");
xn.AppendChild(child);
}
I think that
XmlNode TechReport = originalXml.SelectSingleNode("Details");
should be:
XmlNode TechReport = originalXml.SelectSingleNode("tblTemp/Details");
Or to be more precise:
XmlDocument originalXml = new XmlDocument();
originalXml.Load(strDBDir);
var TechReport = originalXml.SelectSingleNode($"tblTemp/Details[AId={AId}][LoginId={LoginId}]");
if (TechReport != null)
{
XmlNode Data = originalXml.CreateNode(XmlNodeType.Element, "FDate", null);
TechReport.AppendChild(Data);
originalXml.Save(strDBDir);
}
else
{
// Handle this as you see fit...
}

How to change value in xml string?

I have the following method
string UpdateXmlString(string xmlString) {...}
I would like to find all tags which name contain password and delete a value;
Before:
<job xmlns:i=\"...\" xmlns=\"...">
<password>asdfasdf</password>
<adminPassword>asd</adminPassword>
...</job>
Expected result:
<job xmlns:i=\"..." xmlns=\"...">
<password></password>
<adminPassword></adminPassword>
...</job>
How to implement this method?
You should simply be using XmlDocument or XDocument to parse this. I wouldn't manipulate XML strings manually.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace XDocumentTest
{
class Program
{
static void Main(string[] args)
{
try
{
String xml = "<?xml version=\"1.0\"?><rootElement>";
xml += "<user id=\"1\"><password>temp</password></user>";
xml += "<user id=\"2\"><adminPassword>foobar</adminPassword></user>";
xml += "<user id=\"3\"><somePassWORDelement>foobarXYZ</somePassWORDelement></user>";
xml += "</rootElement>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants().Where(
e => e.Name.ToString().ToLower().Contains("password")))
{
Console.WriteLine(element);
// Delete your value here. Either changing the text node
// or by removing the password node. E.g.
element.Value = string.Empty;
}
Console.WriteLine(doc.ToString());
}
catch (Exception e)
{
Console.WriteLine(e);
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
}
}
}
}
You should use XPathNavigator.
In MSDN are some examples that will help you:
https://msdn.microsoft.com/en-us/library/zx28tfx1(v=vs.110).aspx
var doc = XDocument.Load(path);
var element = doc.Descendants("YOUR_Descendants")
.Where(arg => arg.Attribute("ExampleID").Value == "3" )//
.Single();
element.Element("UpdateElements")
.Element("UpdateElements_fc").Value = "222";// update
doc.Save(path); //save

Categories