Get variation from Amazon MWS XML product feed - c#

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

Related

Concatenate values from the same nodes for each employee individ in XML

I am beginner in programming. I have a scenario like this:
For each unique application member, I want to return a new XML that contains for each EMPLOYEE_MEMBER_INDIVID all concatenated values of EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION field.
Roles should be concatenated for each MEMBER_UNIQID from APPLICATION_MEMBER nodes.
<EMPLOYEE_MEMBER_INDIVID>
<EMPLOYEE_MEMBER_INDIVID_MAIN_DATA ENTITY="">
<EMPLOYEE_MEMBER_INDIVID_UNIQ_ID>096788</EMPLOYEE_MEMBER_INDIVID_UNIQ_ID>
<EMPLOYEE_MEMBER_INDIVID_NAME>Dina</EMPLOYEE_MEMBER_INDIVID_NAME>
<EMPLOYEE_MEMBER_INDIVID_SURNAME>Gomez</EMPLOYEE_MEMBER_INDIVID_SURNAME>
</EMPLOYEE_MEMBER_INDIVID_MAIN_DATA>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA ENTITY="">
<EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>Co-borrower</EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA ENTITY=""> <EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>Guarantor</EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA ENTITY="">
<EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>Mortgager individual</EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S>
</EMPLOYEE_MEMBER_INDIVID>
The output node should be:
<EMPLOYEE_MEMBER_INDIVID>
<EMPLOYEE_MEMBER_INDIVID_MAIN_DATA ENTITY="">
<EMPLOYEE_MEMBER_INDIVID_UNIQ_ID>096788</EMPLOYEE_MEMBER_INDIVID_UNIQ_ID>
<EMPLOYEE_MEMBER_INDIVID_NAME>Dina</EMPLOYEE_MEMBER_INDIVID_NAME>
<EMPLOYEE_MEMBER_INDIVID_SURNAME>Gomez</EMPLOYEE_MEMBER_INDIVID_SURNAME>
</EMPLOYEE_MEMBER_INDIVID_MAIN_DATA>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S>
<EMPLOYEE_MEMBER_INDIVID_ROLE_DATA ENTITY="">
<EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>Co-borrower / Guarantor / Mortgager individual</EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA>
</EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S>
</EMPLOYEE_MEMBER_INDIVID>
My code is:
static void Main(string[] args)
{
try
{
//Create A XML Document Of Response String
XmlDocument xmlDoc = new XmlDocument();
//Read the XML File
XmlNodeList nodeList2 = xmlDoc.SelectNodes("//EMPLOYEE_MEMBER_S/EMPLOYEE_MEMBER" +
"[(EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION ='Borrower' " +
"or EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION='Mortgager' " +
"or EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION='Co-borrower')]");
List<string> baseMemberUNIQ_IDs = new List<string>();
List<LoanMember> infos = new List<LoanMember>();
XmlNodeList baseMembersList = xmlDoc.SelectNodes("//APPLICATION_MEMBERS/APPLICATION_MEMBER[ROLE='Borrower' or ROLE='Mortgager individual' or ROLE='Co-borrower']");
foreach (XmlNode xmlNode in baseMembersList)
{
baseMemberUNIQ_IDs.Add(xmlNode["MEMBER_UNIQ_ID"].InnerText);
}
var distinctBaseMembersUNIQ_ID = baseMemberUNIQ_IDs.Distinct();
foreach (var UNIQ_ID in distinctBaseMembersUNIQ_ID)
{
XmlNodeList nodeList = xmlDoc.SelectNodes("//EMPLOYEE_MEMBER_S/EMPLOYEE_MEMBER" +
"[(EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION ='Borrower' " +
"or EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION='Mortgager individual' " +
"or EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S/EMPLOYEE_MEMBER_INDIVID_ROLE_DATA/EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION='Co-borrower') " +
"and EMPLOYEE_MEMBER_INDIVID/EMPLOYEE_MEMBER_INDIVID_MAIN_DATA/EMPLOYEE_MEMBER_INDIVID_UNIQ_ID=" + UNIQ_ID.ToString() + "]");
foreach (XmlNode xmlNode2 in nodeList)
{
String ROLE = "";
foreach (XmlNode childNode in xmlNode2)
{
ROLE = childNode.ChildNodes[0].InnerXml;
Console.WriteLine("CONCATED ROLES ARE " + ROLE);
// All roles of each employee individ should be concatenated inside the first node EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION node, Other nodes shoud be removed/
}
}
}
}
catch
{
throw;
}
Console.ReadKey();
}
}
In the following url is input document XML: https://codebeautify.org/xmlviewer/cb7a26e5
Thank you for your help!
Try following 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);
List<XElement> EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S = doc.Descendants("EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S").ToList();
foreach (XElement EMPLOYEE_MEMBER_INDIVID_ROLE_DATA in EMPLOYEE_MEMBER_INDIVID_ROLE_DATA_S)
{
string[] roles = EMPLOYEE_MEMBER_INDIVID_ROLE_DATA.Descendants("EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION").Select(x => (string)x).ToArray();
XElement newEMPLOYEE_MEMBER_INDIVID_ROLE_DATA = new XElement("EMPLOYEE_MEMBER_INDIVID_ROLE_DATA", new object[] {
new XAttribute("ENTITY", ""),
new XElement("EMPLOYEE_MEMBER_INDIVID_ROLE_ON_APPLICATION", string.Join(" / ", roles))
});
EMPLOYEE_MEMBER_INDIVID_ROLE_DATA.ReplaceWith(newEMPLOYEE_MEMBER_INDIVID_ROLE_DATA);
}
}
}
}

Adding a new node (taking value from another xml file) inside a certain parent node?

I'm trying to create a program having the following steps:
1) Get all xml files from a user given path
2) Open each of the files (if any) and search for nodes <institution> where it is in the format <funding-source><institution-wrap><institution>...</institution></institution-wrap></funding-source>
3) Get the value of the nodes <institution> and search the exact value in the database xml inside the nodes <skosxl:literalForm xml:lang="...">
4) If found, get the attribute value of its parent node <skos:Concept rdf:about="..."> minus the string http://dx.doi.org/
5) Add a node <institution-id institution-id-type="fundref"> in the xml file after the <institution> node with the value like <funding-source><institution-wrap><institution>...</institution><institution-id institution-id-type="fundref">VALUE of the rdf:about attribute</institution-id></institution-wrap></funding-source>
Here is a sample input file and the desired output for that file.
What I have tried:
string pathToUpdatedFile = #"D:\test\Jobs";
var files=Directory.GetFiles(pathToUpdatedFile,"*.xml");
foreach (var file in files)
{
var fundingDoc = XDocument.Load(#"D:\test\database.xml");
XNamespace rdf=XNamespace.Get("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
XNamespace skosxl = XNamespace.Get("http://www.w3.org/2008/05/skos-xl#");
XNamespace skos=XNamespace.Get("http://www.w3.org/2004/02/skos/core#");
var targetAtt = fundingDoc.Descendants(skos+"Concept").Elements(skosxl+"prefLabel")
.ToLookup(s => (string)s.Element(skosxl+"literalForm"), s => (string)s.Parent.Attribute(rdf+"about"));
XDocument outDoc = XDocument.Parse(File.ReadAllText(file),LoadOptions.PreserveWhitespace);
foreach (var f in outDoc.Descendants("funding-source").Elements("institution-wrap"))
{
if (f.Element("institution-id") == null)
{
var name = (string)f.Element("institution");
var x = targetAtt[name].FirstOrDefault(); // just take the first one
if (x != null)
f.Add(new XElement("institution-id", new XAttribute("institution-id-type","fundref"),x.Substring(#"http://dx.doi.org/".Length)));
}
outDoc.Save(file);
}
Console.ReadLine();
But it is not working...Can somebody help...
See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Data;
using System.Data.OleDb;
namespace ConsoleApplication31
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
const string DATABASE = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement article = doc.Root;
XNamespace ns = article.GetDefaultNamespace();
XDocument docDatabase = XDocument.Load(DATABASE);
XElement rdf = docDatabase.Root;
XNamespace nsSkosxl = rdf.GetNamespaceOfPrefix("skosxl");
XNamespace nsRdf = rdf.GetNamespaceOfPrefix("rdf");
List<XElement> prefLabels = rdf.Descendants(nsSkosxl + "prefLabel").ToList();
Dictionary<string, List<string>> dictLabels = prefLabels.GroupBy(x => (string)x.Descendants(nsSkosxl + "literalForm").FirstOrDefault(), y => (string)y.Element(nsSkosxl + "Label").Attribute(nsRdf + "about"))
.ToDictionary(x => x.Key, y => y.ToList());
List<XElement> fundingSources = article.Descendants(ns + "funding-source").ToList();
foreach (XElement fundingSource in fundingSources)
{
XElement institutionWrap = fundingSource.Element(ns + "institution-wrap");
string institution = (string)fundingSource;
if (dictLabels.ContainsKey(institution))
{
institutionWrap.Add(new XElement("institution-id", new object[] {
new XAttribute("institution-id-type","fundref"),
dictLabels[institution]
}));
}
else
{
Console.WriteLine("Dictionary doesn't contain : '{0}'", institution);
}
}
Console.ReadLine();
}
}
}
I think this is what you are looking for (MODIFIED jdweng's CODE A LITTLE)
const string FILENAME = #"c:\temp\test.xml";
const string DATABASE = #"c:\temp\test1.xml";
public static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement article = doc.Root;
XNamespace ns = article.GetDefaultNamespace();
XDocument docDatabase = XDocument.Load(DATABASE);
XElement rdf = docDatabase.Root;
XNamespace nsSkosxl = rdf.GetNamespaceOfPrefix("skosxl");
XNamespace nsSkos = rdf.GetNamespaceOfPrefix("skos");
XNamespace nsRdf = rdf.GetNamespaceOfPrefix("rdf");
List<XElement> prefLabels = rdf.Descendants(nsSkos + "Concept").ToList();
Dictionary<string, List<string>> dictLabels = prefLabels.GroupBy(x => (string)x.Descendants(nsSkosxl + "literalForm").FirstOrDefault(), y => (string)y.Parent.Element(nsSkos+"Concept").Attribute(nsRdf + "about").Value.Substring(18))
.ToDictionary(x => x.Key, y => y.ToList());
List<XElement> fundingSources = article.Descendants(ns + "funding-source").ToList();
foreach (XElement fundingSource in fundingSources)
{
XElement institutionWrap = fundingSource.Element(ns + "institution-wrap");
string institution = (string)fundingSource;
if (dictLabels.ContainsKey(institution))
{
institutionWrap.Add(new XElement("institution-id", new object[] {
new XAttribute("institution-id-type","fundref"),
dictLabels[institution]
}));
}
}
doc.Save(FILENAME);
Console.WriteLine("Done");
Console.ReadLine();
}

Reading XML with namespace and repeating nodes

I have the following XML:
<resource>
<description>TTT</description>
<title>TEST</title>
<entity xmlns="TdmBLRuPlUz.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="TdmBLRuPlUz.xsd TdmBLRuPlUz.xsd">
<UzdProd>
<row>
<F_DAUDZ>50</F_DAUDZ>
<BR_DAUDZ/>
<DAUDZ>50</DAUDZ>
<U_DAUDZ/>
<NKODS>ST2.0_014_023</NKODS>
</row>
</UzdProd>
<UzdMat>
<row>
<NKODS>SAG 2.0_014_150</NKODS>
<NNOSAUK>Sagatave 2.0mm*0.14*150m</NNOSAUK>
<PK_VIEN>1</PK_VIEN>
<DAUDZ>0.077</DAUDZ>
<F_DAUDZ>0.077</F_DAUDZ>
</row>
</UzdMat>
</entity>
</resource>
And this is my C# code:
XNamespace ns = "TdmBLRuPlUz.xsd";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants(ns + "row"))
{
Console.WriteLine(element.Element(ns + "NKODS").Value);
string NKODS = element.Element(ns + "NKODS").Value;
string F_DAUDZ = element.Element(ns + "F_DAUDZ").Value;
string DAUDZ = element.Element(ns + "DAUDZ").Value;
}
What I need is to read values from the XML nodes NKODS, F_DAUDZ and DAUDZ.
The problem is that there are repeating nodes with those names and with this code it gives me the last ones which are under UzdMat node. What would be the way to get the values for these nodes under UzdProd?
I tried to change row to UzdProd, but that didn't work.
You need to read the specific row you want rather than looping through all of them. For example:
var prodRow = doc.Descendants(ns + "UzdProd").Elements(ns + "row").Single();
var matRow = doc.Descendants(ns + "UzdMat").Elements(ns + "row").Single();
var prodNkods = (string) prodRow.Element(ns + "NKODS");
var matNkods = (string) matRow.Element(ns + "NKODS");
See this fiddle for a working demo.
See if this works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication25
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement entity = doc.Descendants().Where(x => x.Name.LocalName == "entity").FirstOrDefault();
XNamespace ns = entity.GetDefaultNamespace();
var results = entity.Elements().Select(x => new {
uzd = x.Name.LocalName,
dict = x.Descendants(ns + "row").Elements().GroupBy(y => y.Name.LocalName, z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).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);

LINQ to read XML

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

Categories