How to get XmlElement from XmlNodeList in C#? - 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();
}
}
}

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();
}
}
}

Unable to parse and read xml data

I have xml format as below. And trying to read elements from content, product name and product id but unable to. Here is what I have tried so far but no luck. Both of my approaches are not working any help is appreciated.
<source xml:base="https://google.com/api/v1" xmlns="http://www.w3.org/2005/Atom" >
<id>s1</id>
<value>
<id>value1</id>
<version>1.90</version>
<content type="application/xml">
<x:products>
<n:Productname>3M</n:Productname>
<n:ProductId n:type="Int32">97</n:ProductId>
</x:products>
<x:products>
<n:Productname>HD</n:Productname>
<n:ProductId n:type="Int32">99</n:ProductId>
</x:products>
</content>
</value>
</source>
FileStream fs = new FileStream(xmlFile, FileMode.Open, FileAccess.Read);
XmlDocument xmldoc = new XmlDocument();
XmlNodeList xmlnodecontent;
xmldoc.Load(fs);
xmlnodecontent = xmldoc.GetElementsByTagName("content");
for (int i = 0; i < xmlnodecontent.Count; i++)
{
var innerXml =xmlnodecontent[i].ChildNodes.Item(0).InnerXml;
//Trying to read product here
}
//Second approach
var doc = XDocument.Load(xmlFile);
var units = from u in doc.Descendants("value")
select new
{
Id = (int)u.Element("id"),
content = from entry in doc.Descendants("content")
select new
{
product = (int)u.Element("d:Product"),
}
};
foreach (var unit in units)
{
var id = unit.Id;
var content = unit.content;
}
I corrected the xml file and used xml linq (XDocument) to get values
<source xml:base="https://google.com/api/v1" xmlns="http://www.w3.org/2005/Atom" xmlns:x="abc" xmlns:n="def" >
<id>s1</id>
<value>
<id>value1</id>
<version>1.90</version>
<content type="application/xml">
<x:products>
<n:Productname>3M</n:Productname>
<n:ProductId n:type="Int32">97</n:ProductId>
</x:products>
<x:products>
<n:Productname>HD</n:Productname>
<n:ProductId n:type="Int32">99</n:ProductId>
</x:products>
</content>
</value>
</source>
Here is the code :
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 FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement value = doc.Descendants().Where(x => x.Name.LocalName == "value").FirstOrDefault();
XNamespace ns = value.GetDefaultNamespace();
XNamespace xNs = value.GetNamespaceOfPrefix("x");
XNamespace nNs = value.GetNamespaceOfPrefix("n");
var values = doc.Descendants(ns + "value").Select(x => new
{
id = (string)x.Element(ns + "id"),
products = x.Descendants(xNs + "products").Select(y => new
{
name = (string)y.Element(nNs + "Productname"),
id = (string)y.Element(nNs + "ProductId")
}).ToList()
}).ToList();
}
}
}

c# - Select node in xmlDocument

I have an XML with this structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Dossiers xmlns:ns2="http://www.dat.de/vxs" source="SD3" type="completeEvaluation">
<ns2:Dossier>
<ns2:Vehicle>
<ns2:VehicleIdentNumber>aaaaaaaaaa</ns2:VehicleIdentNumber>
<ns2:Equipment>
<ns2:OriginalEquipmentValueGross origin="dat">16206.00</ns2:OriginalEquipmentValueGross>
<ns2:SeriesEquipment>
<ns2:EquipmentPosition>
<ns2:DatEquipmentId>15201</ns2:DatEquipmentId>
<ns2:Description>lorem ipsum</ns2:Description>
</ns2:EquipmentPosition>
<ns2:EquipmentPosition>
<ns2:DatEquipmentId>17602</ns2:DatEquipmentId>
<ns2:Description>lorem ipsum</ns2:Description>
</ns2:EquipmentPosition>
...
</ns2:SeriesEquipment>
</ns2:Vehicle>
<ns2:Vehicle>
....
</ns2:Vehicle>
</ns2:Dossier>
</ns2:Dossiers>
With this code I have obtained the ns2:VehicleIdentNumber value:
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlFileContent);
var xmlNodeList = xml.GetElementsByTagName("ns2:Vehicle");
foreach (XmlElement xmlElement in xmlNodeList)
{
var telaio = xmlElement["ns2:VehicleIdentNumber"];
}
but how can I get ns2:OriginalEquipmentValueGross value and ns2:Description value?
Using Xml Linq :
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 FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement root = doc.Root;
XNamespace ns2 = root.GetNamespaceOfPrefix("ns2");
var results = doc.Descendants(ns2 + "Vehicle").Select(x => new
{
vehicleIdentNumber = (string)x.Element(ns2 + "VehicleIdentNumber"),
originalEquipmentValueGross = (string)x.Descendants(ns2 + "OriginalEquipmentValueGross").FirstOrDefault()
}).ToList();
}
}
}
It should be more or less the same of what you already have:
var xmlNodeList = xml.GetElementsByTagName("ns2:Vehicle");
foreach (XmlElement xmlElement in xmlNodeList)
{
var telaio = xmlElement["ns2:VehicleIdentNumber"];
var equipment = xmlElement["ns2:Equipment"];
var originalEquipmentValueGross = equipment["ns2:OriginalEquipmentValueGross"].InnerText;
foreach (XmlElement equipmentPosition in equipment["ns2:SeriesEquipment"].GetElementsByTagName("ns2:EquipmentPosition"))
{
var description = equipmentPosition["ns2:Description"].InnerText;
}
}

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.

Parse from single node in multiple nodes that have same name

<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;
}
}

Categories