I am removing the duplicated entries in one XML file, and the code is removing them and summing the values to have only one entry of each invoice.
But when it reaches the end it removes 3 lines the following ones:
<NumberOfEntries>11972</NumberOfEntries>
<TotalDebit>0</TotalDebit>
<TotalCredit>34422.86</TotalCredit>
So instead of having the final file like:
...
<SourceDocuments>
<SalesInvoices>
<NumberOfEntries>11972</NumberOfEntries>
<TotalDebit>0</TotalDebit>
<TotalCredit>34422.86</TotalCredit>
<Invoice>
<InvoiceNo>FS 006120180101/19959</InvoiceNo>
...
It appears like:
...
<SourceDocuments>
<SalesInvoices>
<Invoice>
<InvoiceNo>FS 006120180101/19959</InvoiceNo>
...
My code is the following one:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
//Ficheiro
const string FILENAME = "ccc.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XNamespace ns = doc.Root.Name.Namespace;
List<XElement> originalInvoices = doc.Descendants(ns + "Invoice").ToList();
var groups = originalInvoices.GroupBy(x => (string)x.Element(ns + "Hash")).ToList();
var finalInvoices = groups.Select(x => new
{
unit = x.Descendants(ns + "UnitPrice").Sum(z => (decimal)z),
credit = x.Descendants(ns + "CreditAmount").Sum(z => (decimal)z),
tax = x.Descendants(ns + "TaxPayable").Sum(z => (decimal)z),
net = x.Descendants(ns + "NetTotal").Sum(z => (decimal)z),
gross = x.Descendants(ns + "GrossTotal").Sum(z => (decimal)z),
first = x.First()
}).ToList();
foreach (var finalInvoice in finalInvoices)
{
finalInvoice.first.Element(ns + "Line").SetElementValue(ns + "UnitPrice", finalInvoice.unit);
finalInvoice.first.Element(ns + "Line").SetElementValue(ns + "CreditAmount", finalInvoice.credit);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "TaxPayable", finalInvoice.tax);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "NetTotal", finalInvoice.net);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "GrossTotal", finalInvoice.gross);
}
doc.Descendants(ns + "SalesInvoices").FirstOrDefault().ReplaceWith(new XElement(ns + "SalesInvoices", finalInvoices.Select(x => x.first)));
doc.Descendants(ns + "SalesInvoices").
Console.WriteLine(doc);
doc.Save("Root.xml");
Console.ReadKey();
}
}
}
And you can see a sample of my XML file here: Pastebin Link
Can someone help me with this, how can I make it to not remove those 3 lines?
The problem is probably on the last line where it writes the file, but I'm not sure.
doc.Descendants(ns + "SalesInvoices").FirstOrDefault().ReplaceWith(new XElement(ns + "SalesInvoices", finalInvoices.Select(x => x.first)));
Small Update on the question:
Well I really think that the problem is on the line above because if I change for example SalesInvoices with TotalCredit which is the last line of the ones that are disappearing the file still wrong but instead of:
...
<SourceDocuments>
<SalesInvoices>
<Invoice>
<InvoiceNo>FS 006120180101/19959</InvoiceNo>
...
I'm getting:
...
<SourceDocuments>
<SalesInvoices>
<NumberOfEntries>11972</NumberOfEntries>
<TotalDebit>0</TotalDebit>
<TotalCredit>
<Invoice>
<InvoiceNo>FS 006120180101/19959</InvoiceNo>
...
there is a missing 34422.86</TotalCredit> before tag <Invoice>
and it's adding the </TotalCredit> after the first closed element </Invoice> as you can test here: Link to test the code
Easy to fix. Just add the missing 3 elements :
foreach (var finalInvoice in finalInvoices)
{
finalInvoice.first.Element(ns + "Line").SetElementValue(ns + "UnitPrice", finalInvoice.unit);
finalInvoice.first.Element(ns + "Line").SetElementValue(ns + "CreditAmount", finalInvoice.credit);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "TaxPayable", finalInvoice.tax);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "NetTotal", finalInvoice.net);
finalInvoice.first.Element(ns + "DocumentTotals").SetElementValue(ns + "GrossTotal", finalInvoice.gross);
}
XElement salesInvoices = doc.Descendants(ns + "SalesInvoices").FirstOrDefault();
XElement numberOfEntries = salesInvoices.Element(ns + "NumberOfEntries");
XElement totalDebit = salesInvoices.Element(ns + "TotalDebit");
XElement totalCredit = salesInvoices.Element(ns + "TotalCredit");
salesInvoices.ReplaceWith(new XElement(ns + "SalesInvoices", new object[] {
numberOfEntries,
totalDebit,
totalCredit,
finalInvoices.Select(x => x.first)
}));
Related
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);
}
}
}
}
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();
}
}
}
I have the following XML file
<eConnect xmlns:dt="urn:schemas-microsoft-com:datatypes">
<SOPTransactionType>
<eConnectProcessInfo>
<ConnectionString>Data Source=DGLSQL1;Initial Catalog=dgl;Persist Security Info=False;Integrated Security=SSPI</ConnectionString>
<EConnectProcsRunFirst>True</EConnectProcsRunFirst>
</eConnectProcessInfo>
<taSopLotAuto_Items>
<taSopLotAuto>
<SOPTYPE>2</SOPTYPE>
<SOPNUMBE>435462</SOPNUMBE>
<LNITMSEQ>16384</LNITMSEQ>
<ITEMNMBR>7740</ITEMNMBR>
<LOCNCODE>18</LOCNCODE>
<QUANTITY>65</QUANTITY>
<LOTNUMBR>15483D0104X68X</LOTNUMBR>
</taSopLotAuto>
</taSopLotAuto_Items>
</SOPTransactionType>
</eConnect>
I am using the following code to read this file
XmlDocument doc = new XmlDocument();
doc.Load("C:\\SOP.XML");
XmlNodeList nodes = doc.SelectNodes("/taSopLotAuto_Items/taSopLotAutoka");
foreach (XmlNode node in nodes)
{
string text = node["SOPTYPE"].InnerText;
Console.WriteLine(text + "\n");
}
Here I want to read the content of <taSopLoAuto>. But I am unable to read the file content. Is this because of top few lines written in document? Please help me to solve the problem.
The namespace is an issue. You can use Linq like below
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 xml =
"<eConnect xmlns:dt=\"urn:schemas-microsoft-com:datatypes\">" +
"<SOPTransactionType>" +
"<eConnectProcessInfo>" +
"<ConnectionString>Data Source=DGLSQL1;Initial Catalog=dgl;Persist Security Info=False;Integrated Security=SSPI</ConnectionString>" +
"<EConnectProcsRunFirst>True</EConnectProcsRunFirst>" +
"</eConnectProcessInfo>" +
"<taSopLotAuto_Items>" +
"<taSopLotAuto>" +
"<SOPTYPE>2</SOPTYPE>" +
"<SOPNUMBE>435462</SOPNUMBE>" +
"<LNITMSEQ>16384</LNITMSEQ>" +
"<ITEMNMBR>7740</ITEMNMBR>" +
"<LOCNCODE>18</LOCNCODE>" +
"<QUANTITY>65</QUANTITY>" +
"<LOTNUMBR>15483D0104X68X</LOTNUMBR>" +
"</taSopLotAuto>" +
"<taSopLotAuto>" +
"<SOPTYPE>2</SOPTYPE>" +
"<SOPNUMBE>435462</SOPNUMBE>" +
"<LNITMSEQ>16384</LNITMSEQ>" +
"<ITEMNMBR>7740</ITEMNMBR>" +
"<LOCNCODE>18</LOCNCODE>" +
"<QUANTITY>65</QUANTITY>" +
"<LOTNUMBR>15483D0104X68X</LOTNUMBR>" +
"</taSopLotAuto>" +
"<taSopLotAuto>" +
"<SOPTYPE>2</SOPTYPE>" +
"<SOPNUMBE>435462</SOPNUMBE>" +
"<LNITMSEQ>16384</LNITMSEQ>" +
"<ITEMNMBR>7740</ITEMNMBR>" +
"<LOCNCODE>18</LOCNCODE>" +
"<QUANTITY>65</QUANTITY>" +
"<LOTNUMBR>15483D0104X68X</LOTNUMBR>" +
"</taSopLotAuto>" +
"</taSopLotAuto_Items>" +
"</SOPTransactionType>" +
"</eConnect>";
XDocument doc = XDocument.Parse(xml);
List<XElement> taSopLotAutos = doc.Descendants()
.Where(x => x.Name.LocalName == "taSopLotAuto")
.ToList();
var results = taSopLotAutos.Select(x => new
{
sopType = (int)x.Element("SOPTYPE"),
sopNumbe = (int)x.Element("SOPNUMBE"),
lnitmsseg = (int)x.Element("LNITMSEQ"),
locncode = (int)x.Element("LOCNCODE"),
quantity = (int)x.Element("QUANTITY"),
lotnumbr = (string)x.Element("LOTNUMBR")
}).ToList();
}
}
}
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);
I need assistance in retrieving the element Country and Its value 1 in the below code.
Thanks in advance.
<?xml version="1.0" encoding="utf-8"?>
<env:Contentname xmlns:env="http://data.schemas" xmlns="http://2013-02-01/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<env:Content action="Hello">
<env:Data xsi:type="Yellow">
</env:Data>
</env:Content>
<env:Content action="Hello">
<env:Data xsi:type="Red">
<Status xmlns="http://2010-10-10/">
<Id >39681</Id>
<Name>Published</Name>
</Status>
</env:Data>
</env:Content>
<env:Content action="Hello">
<env:Data xsi:type="green">
<Document>
<Country>1</Country>
</Document>
</env:Data>
</env:Content>
</env:Body>
</env:Contentname>
I tried this,
var result = from x in root.Descendants(aw + "Data")
where (string)x.Attribute(kw + "type") == "green"
select x;
foreach (var item in result)
{
var str = item.Element("Document").Element("Country");
Console.WriteLine(str.Value);
}
But i am getting error.(Object reference not set to an instance of an object.)
kindly help me with this.
This is the problem, for two reasons:
var str = item.Element("Document").Element("country");
Firstly, XML is case-sensitive - you want Country, not country.
Secondly, those elements inherit the namespace declared with xmlns=... in the root element. You need:
XNamespace ns = "http://2013-02-01/";
...
var element = item.Element(ns + "Document").Element(ns + "Country");
I'd also encourage you to avoid query expressions where they don't actually buy you much. In this case, you could perform the whole query in one go using the Elements extension method which works on sequences of input elements, assuming you don't mind finding every Document -> Country element rather than just one per Data:
var query = root.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Elements(ns + "Document")
.Elements(ns + "Country")
.Select(x => x.Value);
foreach (var item in query)
{
Console.WriteLine(item);
}
One significant difference - this won't fall over with an exception if there is a Data element with xsi:type='green' which doesn't have a Document -> Country element. If you want it to (to find bad data) you could use:
var query = root.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Select(x => x.Element(ns + "Document")
.Element(ns + "Country")
.Value);
To show a short but complete example, this code (with your XML as test.xml) gives an output of "1":
using System;
using System.Linq;
using System.Xml.Linq;
public class Program
{
static void Main(string[] args)
{
var doc = XDocument.Load("test.xml");
XNamespace ns = "http://2013-02-01/";
XNamespace kw = "http://www.w3.org/2001/XMLSchema-instance";
XNamespace aw = "http://data.schemas";
var query = doc.Descendants(aw + "Data")
.Where(x => (string)x.Attribute(kw + "type") == "green")
.Elements(ns + "Document")
.Elements(ns + "Country")
.Select(x => x.Value);
foreach (var item in query)
{
Console.WriteLine(item);
}
}
}