LINQ to read XML - c#

I am using this XML file:
<root>
<level1 name="A">
<level2 name="A1" />
<level2 name="A2" />
</level1>
<level1 name="B">
<level2 name="B1" />
<level2 name="B2" />
</level1>
<level1 name="C" />
</root>
Could someone give me a C# code using LINQ, the simplest way to print this result:
(Note the extra space if it is a level2 node)
A
A1
A2
B
B1
B2
C
Currently I have written this code:
XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
select lv1.Attribute("name").Value;
foreach (var lv1 in lv1s)
{
result.AppendLine(lv1);
var lv2s = from lv2 in xdoc...???
}

Try this.
using System.Xml.Linq;
void Main()
{
StringBuilder result = new StringBuilder();
//Load xml
XDocument xdoc = XDocument.Load("data.xml");
//Run query
var lv1s = from lv1 in xdoc.Descendants("level1")
select new {
Header = lv1.Attribute("name").Value,
Children = lv1.Descendants("level2")
};
//Loop through results
foreach (var lv1 in lv1s){
result.AppendLine(lv1.Header);
foreach(var lv2 in lv1.Children)
result.AppendLine(" " + lv2.Attribute("name").Value);
}
Console.WriteLine(result);
}

Or, if you want a more general approach - i.e. for nesting up to "levelN":
void Main()
{
XElement rootElement = XElement.Load(#"c:\events\test.xml");
Console.WriteLine(GetOutline(0, rootElement));
}
private string GetOutline(int indentLevel, XElement element)
{
StringBuilder result = new StringBuilder();
if (element.Attribute("name") != null)
{
result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
}
foreach (XElement childElement in element.Elements())
{
result.Append(GetOutline(indentLevel + 1, childElement));
}
return result.ToString();
}

A couple of plain old foreach loops provides a clean solution:
foreach (XElement level1Element in XElement.Load("data.xml").Elements("level1"))
{
result.AppendLine(level1Element.Attribute("name").Value);
foreach (XElement level2Element in level1Element.Elements("level2"))
{
result.AppendLine(" " + level2Element.Attribute("name").Value);
}
}

Here are a couple of complete working examples that build on the #bendewey & #dommer examples. I needed to tweak each one a bit to get it to work, but in case another LINQ noob is looking for working examples, here you go:
//bendewey's example using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ1
{
static void Main( )
{
//Load xml
XDocument xdoc = XDocument.Load(#"c:\\data.xml"); //you'll have to edit your path
//Run query
var lv1s = from lv1 in xdoc.Descendants("level1")
select new
{
Header = lv1.Attribute("name").Value,
Children = lv1.Descendants("level2")
};
StringBuilder result = new StringBuilder(); //had to add this to make the result work
//Loop through results
foreach (var lv1 in lv1s)
{
result.AppendLine(" " + lv1.Header);
foreach(var lv2 in lv1.Children)
result.AppendLine(" " + lv2.Attribute("name").Value);
}
Console.WriteLine(result.ToString()); //added this so you could see the output on the console
}
}
And next:
//Dommer's example, using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ
{
static void Main( )
{
XElement rootElement = XElement.Load(#"c:\\data.xml"); //you'll have to edit your path
Console.WriteLine(GetOutline(0, rootElement));
}
static private string GetOutline(int indentLevel, XElement element)
{
StringBuilder result = new StringBuilder();
if (element.Attribute("name") != null)
{
result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
}
foreach (XElement childElement in element.Elements())
{
result.Append(GetOutline(indentLevel + 1, childElement));
}
return result.ToString();
}
}
These both compile & work in VS2010 using csc.exe version 4.0.30319.1 and give the exact same output. Hopefully these help someone else who's looking for working examples of code.
EDIT: added #eglasius' example as well since it became useful to me:
//#eglasius example, still using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
class loadXMLToLINQ2
{
static void Main( )
{
StringBuilder result = new StringBuilder(); //needed for result below
XDocument xdoc = XDocument.Load(#"c:\\deg\\data.xml"); //you'll have to edit your path
var lv1s = xdoc.Root.Descendants("level1");
var lvs = lv1s.SelectMany(l=>
new string[]{ l.Attribute("name").Value }
.Union(
l.Descendants("level2")
.Select(l2=>" " + l2.Attribute("name").Value)
)
);
foreach (var lv in lvs)
{
result.AppendLine(lv);
}
Console.WriteLine(result);//added this so you could see the result
}
}

XDocument xdoc = XDocument.Load("data.xml");
var lv1s = xdoc.Root.Descendants("level1");
var lvs = lv1s.SelectMany(l=>
new string[]{ l.Attribute("name").Value }
.Union(
l.Descendants("level2")
.Select(l2=>" " + l2.Attribute("name").Value)
)
);
foreach (var lv in lvs)
{
result.AppendLine(lv);
}
Ps. You have to use .Root on any of these versions.

Asynchronous loading of the XML file can improve performance, especially if the file is large or if it takes a long time to load. In this example, we use the XDocument.LoadAsync method to load and parse the XML file asynchronously, which can help to prevent the application from becoming unresponsive while the file is being loaded.
Demo: https://dotnetfiddle.net/PGFE7c (using XML string parsing)
Implementation:
XDocument doc;
// Open the XML file using File.OpenRead and pass the stream to
// XDocument.LoadAsync to load and parse the XML asynchronously
using (var stream = File.OpenRead("data.xml"))
{
doc = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None);
}
// Select the level1 elements from the document and create an anonymous object for each element
// with a Name property containing the value of the "name" attribute and a Children property
// containing a collection of the names of the level2 elements
var results = doc.Descendants("level1")
.Select(level1 => new
{
Name = level1.Attribute("name").Value,
Children = level1.Descendants("level2")
.Select(level2 => level2.Attribute("name").Value)
});
foreach (var result in results)
{
Console.WriteLine(result.Name);
foreach (var child in result.Children)
Console.WriteLine(" " + child);
}
Result:
A
  A1
  A2
B
  B1
  B2
C

Related

Get variation from Amazon MWS XML product feed

I'm using the Amazon MWS C# Client library and I can successfully return an XML feed but I cannot seem to parse the Relationships elements whatsoever. This is the official Product API documentation
Here is the XML returned to me:
<Product>
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>A1F83G8C2ARO7P</MarketplaceId>
<ASIN>B0769TXBK4</ASIN>
</MarketplaceASIN>
</Identifiers>
<AttributeSets>
<ns2:ItemAttributes xmlns:ns2="http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd" xml:lang="en-GB">
<ns2:Brand>Ambi Pur</ns2:Brand>
<ns2:Label>Ambi Pur</ns2:Label>
<ns2:Manufacturer>Ambi Pur</ns2:Manufacturer>
<ns2:NumberOfItems>1</ns2:NumberOfItems>
<ns2:PackageDimensions>
<ns2:Height Units="inches">2.5590551155</ns2:Height>
<ns2:Length Units="inches">6.6929133790</ns2:Length>
<ns2:Width Units="inches">4.5275590505</ns2:Width>
<ns2:Weight Units="pounds">0.2645547144</ns2:Weight>
</ns2:PackageDimensions>
<ns2:PackageQuantity>2</ns2:PackageQuantity>
<ns2:PartNumber>95535</ns2:PartNumber>
<ns2:ProductGroup>BISS Basic</ns2:ProductGroup>
<ns2:ProductTypeName>ADHESIVES_AND_SEALANTS</ns2:ProductTypeName>
<ns2:Publisher>Ambi Pur</ns2:Publisher>
<ns2:SmallImage>
<ns2:URL>http://ecx.images-amazon.com/images/I/41DhkI6B8oL._SL75_.jpg</ns2:URL>
<ns2:Height Units="pixels">75</ns2:Height>
<ns2:Width Units="pixels">75</ns2:Width>
</ns2:SmallImage>
<ns2:Studio>Ambi Pur</ns2:Studio>
<ns2:Title>Ambi Pur 3Volution Air Freshener - 1 Plug-In Diffuser (2 Packs)</ns2:Title>
</ns2:ItemAttributes>
</AttributeSets>
<Relationships>
<VariationParent xmlns="http://mws.amazonservices.com/schema/Products/2011-10-01">
<Identifiers>
<MarketplaceASIN>
<MarketplaceId>A1F83G8C2ARO7P</MarketplaceId>
<ASIN>B076B1GP37</ASIN>
</MarketplaceASIN>
</Identifiers>
</VariationParent>
</Relationships>
<SalesRankings>
<SalesRank>
<ProductCategoryId>biss_basic_display_on_website</ProductCategoryId>
<Rank>1866</Rank>
</SalesRank>
<SalesRank>
<ProductCategoryId>301308031</ProductCategoryId>
<Rank>832</Rank>
</SalesRank>
</SalesRankings>
</Product>
Here is the code, the AttributeSets foreach works fine but I cannot seem to traverse the Relationships elements without getting 'Object reference not set to an instance of an object'.
ListMatchingProductsResponse lmpr = (ListMatchingProductsResponse)response;
foreach (var x in lmpr.ListMatchingProductsResult.Products.Product)
{
Console.WriteLine("ASIN: " + x.Identifiers.MarketplaceASIN.ASIN);
int i = 0;
SalesRankList rankings = x.SalesRankings;
List<SalesRankType> salesRankList = rankings.SalesRank;
foreach (SalesRankType saleRankings in salesRankList)
{
for (; i < 1; i++)
{
if (saleRankings.IsSetRank())
{
Console.WriteLine("Sales Rank: " + Convert.ToString(saleRankings.Rank));
}
}
}
foreach (var relationship in x.Relationships.Any)
{
string xml = ProductsUtil.FormatXml((System.Xml.XmlElement)relationship);
XElement element = XElement.Parse(xml);
XNamespace ns2 = "{http://mws.amazonservices.com/schema/Products/2011-10-01}";
// Errors here!
Console.WriteLine(element.Element(ns2 + "VariationParent").Element(ns2 + "Identifiers"));
}
foreach (var attribute in x.AttributeSets.Any)
{
string xml = ProductsUtil.FormatXml((System.Xml.XmlElement)attribute);
XElement element = XElement.Parse(xml);
XNamespace ns2 = "http://mws.amazonservices.com/schema/Products/2011-10-01/default.xsd";
Console.WriteLine("Title: " + element.Element(ns2 + "Title").Value);
Console.WriteLine("Title: " + element.Element(ns2 + "Title").Name);
Console.WriteLine("Pack Quantity: " + element.Element(ns2 + "PackageQuantity").Value);
if ((string)element.Element(ns2 + "ListPrice") != null)
{
Console.WriteLine("List Price:" + element.Element(ns2 + "ListPrice").Element(ns2 + "Amount").Value);
}
}
lstASIN.ASIN.Add(x.Identifiers.MarketplaceASIN.ASIN);
Console.WriteLine("");
}
}
Try following :
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 relationships = doc.Descendants("Relationships").FirstOrDefault();
XNamespace ns = "http://mws.amazonservices.com/schema/Products/2011-10-01";
string marketplaceId = (string)relationships.Descendants(ns + "MarketplaceId").FirstOrDefault();
}
}
}
This seems to work but still feels like unnecessary traversion.
RelationshipList rlist = x.Relationships;
if (rlist.IsSetAny())
{
foreach (var relationship in rlist.Any)
{
string xml = ProductsUtil.FormatXml((System.Xml.XmlElement)relationship);
XDocument xDoc = XDocument.Parse(xml);
XNamespace ns2 = "http://mws.amazonservices.com/schema/Products/2011-10-01";
IEnumerable<object> relationships = xDoc.Descendants();
foreach (System.Xml.Linq.XElement xe in relationships)
{
if (xe.Name == ns2 + "ASIN")
{
Console.WriteLine(xe.Value);
}
}
}
}

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.

Getting Values from XML

I have an XML File that is filled with values that look like :
<property name ="Web Application" value="\Fxnet2\Web\webpl\" />
<property name="Web Service" value="\FXnet2\Web\FXnet_SC_WS\" />
For each line i would like to import the name into one string (Lets call it serviceName) and the value in a different one (Lets call it servicePath)
I got around 250 lines of the sort in the xml , is it possible to do it in the current xml format? and if it does how ? , or should i change the format of my list?
Thanks in advance .
You can get all nodes and access their attributes in a loop. In the example below I'm adding the values of both attributes to 2 different arrays that you can easily manipulate later.
XmlDocument doc = new XmlDocument();
doc.Load("yourfile.xml");
XmlNodeList usernodes = doc.SelectNodes("//property");
//Declare arrays
List<string> serviceName = new List<string>();
List<string> servicePath = new List<string>();
//iterate through all elements found
foreach (XmlNode usernode in usernodes)
{
serviceName.Add(usernode.Attributes["name"].Value);
serviceName.Add(usernode.Attributes["value"].Value);
}
Managed to Do it in the end
static void Main(string[] args)
{
List<Service> services;
using(StreamReader file = File.OpenText("c:\\projects.xml"))
{
XDocument doc = XDocument.Load(file);
services = (from node in doc.Root.Elements("property")
select new Service
{
serviceName = node.Attribute("name").Value,
servicePath = node.Attribute("value").Value,
dllFiles = System.IO.Directory.GetFiles( "servicePath", "*.dll" ),
}).ToList<Service>();
}
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
{
static void Main(string[] args)
{
string input =
"<Root>" +
"<property name =\"Web Application\" value=\"\\Fxnet2\\Web\\webpl\" />" +
"<property name=\"Web Service\" value=\"\\FXnet2\\Web\\FXnet_SC_WS\" />" +
"</Root>";
XElement root = XElement.Parse(input);
var results = root.Descendants("property").Select(x => new {
name = x.Attribute("name").Value,
value = x.Attribute("value").Value
}).ToList();
}
}
}
​

Getting detail of XML node when I have found the relevant parent node?

I'm almost getting there, but I need some help.
This is the code that I use to process our XML file. I'm able to find the section that I need to store; I just don't know how to save it.
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
doc.Load(#"c:\xml\Sales.xml");
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("nd/ni/nv/noid");
foreach (XmlNode node in nodes)
{
if (node.OuterXml.IndexOf("Server=1,Function=1,Location=") > 0)
{
Console.WriteLine(node.OuterXml);
// This prints out "<noid>Server=1,Function=1,Location=24</noid>"
// How do I read the four <r> nodes within this <noid>?
// The values would be [124, 2, 43, 90]
}
}
The xml looks something like this:
<nd>
<ni>
<nv>
<noid>Managed=1,Network=1,smtp=1</noid>
<r>27</r>
<r>4</r>
</nv>
<nv>
<noid>Managed=1,Network=1,Ibc=1</noid>
<r>8</r>
<r>2</r>
</nv>
<nv>
<noid>Server=1,Function=1,Location=24</noid>
<r>124</r>
<r>2</r>
<r>43</r>
<r>90</r>
</nv>
<nv>
<noid>Unmanaged=9,Label=7,Place=5</noid>
<r>10</r>
<r>20</r>
</nv>
</ni>
</nd>
Console.WriteLine prints the correct <noid> text, so I know that I've already found the section with the relevant data.
My question is, how can I read the four <r> inside this <noid>? Ideally, within the IF statement, how can I read all the <r> elements that are between the <nv></nv>?
Thanks.
Using Linq-to-xml
var xmlText = File.ReadAllText(#"C:\YourDirectory\YourFile.xml");
var xDoc = XDocument.Parse(xmlText);
var rValues = new List<string>(); //THIS IS YOUR RESULT
var nvNodes = xDoc.Descendants("nv");
foreach(var el in nvNodes)
{
if (el.Element("noid").Value.Contains("Server=1,Function=1,Location="))
rValues = el.Elements("r").Select(e => e.Value).ToList();
}
Or, replacing the foreach with Linq (fails if First() is not satisfied)
var rValues = nvNodes.
First(nv => nv.Value.Contains("Server=1,Function=1,Location="))
.Elements("r")
.Select(r => r.Value);
A non-optimized, non-linq solution
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
doc.Load(#"C:\YourDirectory\YourFile.xml");
var rValues = new List<string>(); //THIS IS YOUR RESULT
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("//nd/ni/nv");
foreach (XmlNode node in nodes)
{
if (node.FirstChild.InnerText.Contains("Server=1,Function=1,Location="))
{
foreach(XmlNode childnode in node.ChildNodes)
{
if (childnode.Name == "r")
rValues.Add(childnode.InnerText);
}
}
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input =
"<nd>" +
"<ni>" +
"<nv>" +
"<noid>Managed=1,Network=1,smtp=1</noid>" +
"<r>27</r>" +
"<r>4</r>" +
"</nv>" +
"<nv>" +
"<noid>Managed=1,Network=1,Ibc=1</noid>" +
"<r>8</r>" +
"<r>2</r>" +
"</nv>" +
"<nv>" +
"<noid>Server=1,Function=1,Location=24</noid>" +
"<r>124</r>" +
"<r>2</r>" +
"<r>43</r>" +
"<r>90</r>" +
"</nv>" +
"<nv>" +
"<noid>Unmanaged=9,Label=7,Place=5</noid>" +
"<r>10</r>" +
"<r>20</r>" +
"</nv>" +
"</ni>" +
"</nd>";
XElement nd = XElement.Parse(input);
var results = nd.Descendants("nv").Select(x => new
{
noid = (string)x.Element("noid"),
r = x.Elements("r").Select(y => (int)y).ToList()
}).ToList();
}
}
}
​
A short, but difficult to understand XPath expression:
XmlNodeList rNodes = root.SelectNodes(
"nd/ni/nv[noid/text()[contains(.,'Server=1,Function=1,Location=')]]/r");
foreach (XmlNode rNode in rNodes)
Console.WriteLine(rNode.InnerText);

Failing To Parse the XML File in C#

I am a c++ developer and I started working on a C# WPF project. I have a method which should read the xml file. In my c++ application I could do it very efficiently but in WPF I am not sure how to approach the problem. Let me show you my code:
// When Browse Button is clicked this method is called
private void ExecuteScriptFileDialog()
{
var dialog = new OpenFileDialog { InitialDirectory = _defaultPath };
dialog.DefaultExt = ".xml";
dialog.Filter = "XML Files (*.xml)|*.xml";
dialog.ShowDialog();
ScriptPath = dialog.FileName; //ScriptPath contains the Path of the Xml File
if (File.Exists(ScriptPath))
{
LoadAardvarkScript(ScriptPath);
}
}
public void LoadAardvarkScript(string ScriptPath)
{
// I should read the xml file
}
I had achieved in C++ as follows:
File file = m_selectScript->getCurrentFile(); //m_selectScript is combobox name
if(file.exists())
{
LoadAardvarkScript(file);
}
void LoadAardvarkScript(File file)
{
XmlDocument xmlDoc(file);
//Get the main xml element
XmlElement *mainElement = xmlDoc.getDocumentElement();
XmlElement *childElement = NULL;
XmlElement *e = NULL;
int index = 0;
if(!mainElement )
{
//Not a valid XML file.
return ;
}
//Reading configurations...
if(mainElement->hasTagName("aardvark"))
{
forEachXmlChildElement (*mainElement, childElement)
{
//Read Board Name
if (childElement->hasTagName ("i2c_write"))
{
// Some code
}
}
}
How can I get the tagname of both mainElement and childelem as done in my c++ code? :)
not knowing the layout of your xml file here is an example that you can use if you know how to use Linq
class Program
{
static void Main(string[] args)
{
XElement main = XElement.Load(#"users.xml");
var results = main.Descendants("User")
.Descendants("Name")
.Where(e => e.Value == "John Doe")
.Select(e => e.Parent)
.Descendants("test")
.Select(e => new { date = e.Descendants("Date").FirstOrDefault().Value, points = e.Descendants("points").FirstOrDefault().Value });
foreach (var result in results)
Console.WriteLine("{0}, {1}", result.date, result.points);
Console.ReadLine();
}
}
you could also use XPATH as well to parse xml file.. but would really need to see the xml file layout
if you want to do it using xmlreader
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace XmlReading
{
class Program
{
static void Main(string[] args)
{
//Create an instance of the XmlTextReader and call Read method to read the file
XmlTextReader textReader = new XmlTextReader("C:\\myxml.xml");
textReader.Read();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(textReader);
XmlNodeList BCode = xmlDoc.GetElementsByTagName("Brandcode");
XmlNodeList BName = xmlDoc.GetElementsByTagName("Brandname");
for (int i = 0; i < BCode.Count; i++)
{
if (BCode[i].InnerText == "001")
Console.WriteLine(BName[i].InnerText);
}
Console.ReadLine();
}
}
}
Use LINQ queries to extract data from xml (XDocument)
Refer this link for further insights on XLinq.
Sample XML :
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<People>
<Person id="1">
<Name>Joe</Name>
<Age>35</Age>
<Job>Manager</Job>
</Person>
<Person id="2">
<Name>Jason</Name>
<Age>18</Age>
<Job>Software Engineer</Job>
</Person>
</People>
Sample Linq query:
var names = (from person in Xdocument.Load("People.xml").Descendants("Person")
where int.Parse(person.Element("Age").Value) < 30
select person.Element("Name").Value).ToList();
Names will be list of string. This query will return Jason as he is 18 yrs old.
Using Linq2Xml,
var xDoc = XDocument.Load("myfile.xml");
var result = xDoc.Descendants("i2c_write")
.Select(x => new
{
Addr = x.Attribute("addr").Value,
Count = x.Attribute("count").Value,
Radix = x.Attribute("radix").Value,
Value = x.Value,
Sleep = ((XElement)x.NextNode).Attribute("ms").Value
})
.ToList();
var khz = xDoc.Root.Element("i2c_bitrate").Attribute("khz").Value;

Categories