I have a rather complex XML document
<ItemSearchResponse xmlns="-------">
<OperationRequest>
<HTTPHeaders>
</HTTPHeaders>
<RequestId>0S57WGDPNC7T8HNBV76K</RequestId>
<Arguments>
</Arguments>
<RequestProcessingTime>0.441776990890503</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<ItemSearchRequest>
</ItemSearchRequest>
</Request>
<TotalResults>1020</TotalResults>
<TotalPages>102</TotalPages>
<Item>
<ASIN>B004WL0L9S</ASIN>
<SalesRank>1</SalesRank>
<ItemAttributes>
<Manufacturer>Georgia Pacific Consumer Products LP (Cut-Sheet Paper)</Manufacturer>
<Title>GP Copy & Print Paper, 8.5 x 11 Inches Letter Size, 92 Bright White, 20 Lb, Ream of 500 Sheets (998067R)</Title>
<ItemAttributes>
</Item>
<Items>
I run this query over it and it returns of list of zero:
XDocument doc = XDocument.Load(url);
List<Item> Items = (from c in doc.Elements("Item")
select new Item
{
Title = c.Element("Title").Value
SaleRank = c.Element("SaleRank").Value
ASIN = c.Element("ASIN").Value
}).ToList<Item>();
I am really new to XLinq but according to the documentation that should work.
First of all you have not valid xml. Here is example how it should looks:
<ItemSearchResponse >
<OperationRequest>
<HTTPHeaders> </HTTPHeaders>
<RequestId>0S57WGDPNC7T8HNBV76K</RequestId>
<Arguments> </Arguments>
<RequestProcessingTime>0.441776990890503</RequestProcessingTime>
</OperationRequest>
<Items>
<Request>
<ItemSearchRequest> </ItemSearchRequest>
</Request>
<TotalResults>1020</TotalResults>
<TotalPages>102</TotalPages>
<Item>
<ASIN>B004WL0L9S</ASIN>
<SalesRank>1</SalesRank>
<ItemAttributes>
<Manufacturer>Georgia Pacific Consumer Products LP (Cut-Sheet Paper)</Manufacturer>
<Title>GP Copy & Print Paper, 8.5 x 11 Inches Letter Size, 92 Bright White, 20 Lb, Ream of 500 Sheets (998067R)</Title>
</ItemAttributes>
</Item>
</Items>
</ItemSearchResponse>
And this code works with it:
var Items = (from c in doc.Root.Element("Items").Elements("Item")
select new Item()
{
Title = c.Element("ItemAttributes").Element("Title").Value,
SaleRank = c.Element("SalesRank").Value,
ASIN = c.Element("ASIN").Value
}).ToList();
or with Descendants:
var Items = (from c in doc.Root.Descendants("Item")
select new
{
Title = c.Element("ItemAttributes").Element("Title").Value,
SaleRank = c.Element("SalesRank").Value,
ASIN = c.Element("ASIN").Value
}).ToList();
The problem lies in your use of .Elements(XName) which it seems you're assuming is recursive. It is not, It only inspects direct children of the target (in this case the document).
I believe the function you're looking for is .Descendants(XName) which will look recursively, but be warned this is not very fast on massive XML docs.
The problem is the difference between Elements and Descendants
var items = (from c in doc.Descendants("Item")
select new Item
{
Title = c.Element("ItemAttributes").Element("Title").Value,
SaleRank = c.Element("SalesRank").Value,
ASIN = c.Element("ASIN").Value
}).ToList<Item>();
Elements a list of Xml Elements directly underneath the current Element (i.e. only 1 level deep). Descendants will retrieve all elements.
Based on your Xml and your query, I showed the use of Descendant and Element. The initial loop uses Descendants(), but access to Title uses to chained Element() calls.
I tried running the code, and I'm assuming the xmlns was removed for posting, but your actual code has a real namespace in it. If not, that maybe a problem you need to resolve first.
Turns out I need to add the namespace to query.
XNamespace ns = NAMESPACE;
var items = (from c in doc.Descendants(ns +"Item")
select new Item
{
Title = c.Element(ns + "ItemAttributes").Element(ns + "Title").Value,
SalesRank = c.Element(ns +"SalesRank").Value,
ASIN = c.Element(ns + "ASIN").Value
}).ToList<Item>();
Related
Looking to find categoryId and categoryName for first item.
It could also return no products.
XML Looks like this
<findItemsByKeywordsResponse xmlns="http://...">
<ack>Success</ack>
<version>1.13.0</version>
<timestamp>2016-11-10T17:48:21.321Z</timestamp>
<searchResult count="1">
<item>
<itemId>12354</itemId>
<title>ABCD#</title>
<globalId>ddd</globalId>
<primaryCategory>
<categoryId>**1234**</categoryId>
<categoryName>**Catg Nameee**</categoryName>
</primaryCategory>
</item>
</searchResult>
<paginationOutput>
</paginationOutput>
</findItemsByKeywordsResponse>
Full xml here
Because your root element has a namespace defined what you should do when searching for the descendant items is specify the namespace:
XNamespace ns = #"http://www.ebay.com/marketplace/search/v1/services";
var result = XDocument.Load("data.xml")
.Descendants(ns + "item")
.FirstOrDefault()?.Element(ns + "primaryCategory");
var categoryId = result?.Element(ns + "categoryId")?.Value;
var categoryName = result?.Element(ns + "categoryName")?.Value;
Also you can use C# 6.0 Null Propagation (?:) to retrieve the <primaryCategory> in a more elegant way. Keep in mind that if you do not have an <item> tag or a <primaryCategory> tag the result will be equal to null
In addition it might make more sense to try and find the first <item> that answers some predicate. If that is the case change the .FirstOrDefault() with:
.FirstOrDefault(item => item.Element("itemId").Value == "12345") for example
I have an XML file that looks like this -
<SST_SignageCompConfig>
<Items>
<Item>
<Index>0</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Branding-Colours-for-business.jpg</Name>
</Item>
<Item>
<Index>1</Index>
<Type>1</Type>
<Duration>7</Duration>
<Name>Flower of Life Meditation - Copy.png</Name>
</Item>
</Items>
</SST_SignageCompConfig>
I need to count how many Item Elements there are within the Items Element.
ie how many images there are.
I'm using XDocument, so my XML file is loaded like this -
string configurationPath = System.IO.Path.Combine("C:\\SST Software\\DSS\\Compilations\\" + compName + #"\\Comp.cfg");
XDocument filedoc = XDocument.Load(configurationPath);
I've tried numerous variations of the following, with all returning a null object reference exception
foreach (var item in filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Nodes())
{
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Attribute("Name").ToString();
files.Append(name + "|");
}
I've found countless examples of how to count how many different child elements are within an element, but I need to know how many instances of the same element exist.
Can anyone point me in the right direction?
You can select all names like so:
var names = from item in filedoc.Descendants("Item")
select (string)item.Element("Name");
Or without the query syntax:
var names = filedoc.Descendants("Item").Elements("Name").Select(e => e.Value);
You can get only unique names by:
var uniqueNames = names.Distinct();
You're on the right track. Try finding out exactly which invocation is giving you the NullReferenceException. My guess is that it's the attempt to find:
.Element("SST_SignageCompConfig")
Which is your root. Try the following instead:
// note the difference between .Element and .Elements
var count = filedoc.Root.Element("Items").Elements("Item").Count();
You could also use XPath to help you nail down the navigation within your XDocument:
// returns the current top level element
var element = filedoc.Root.XPathSelectElement(".");
// If the returned element is "SST_SignageCompConfig", then:
var nextElement = filedoc.Root.XPathSelectElement("./Items")
// If the "." element is *not* "SST_SignageCompConfig", then try and locate where in your XML document that node is.
// You can navigate up with .Parent and down with .Element(s)
And so on.
How about:
var nav = fileDoc.CreateNavigator();
XPathNodeIterator navShape = nav.Select("/SST_SignageCompConfig/Items");
navShape.MoveNext()
var count = navShape.Count;
If your xml has only one Items element, this should do the trick:
filedoc.Descendants("Item")
.GroupBy(e => e.Element("Name")!=null? e.Element("Name").Value:String.Empty)
.Select(g => new
{
Name = g.Key,
Count = g.Count()
});
Because "Name" is an element and not an attribute of your xml structure.
can you try replacing this?
string name = filedoc.Element("SST_SignageCompConfig").Element("Items").Element("Item").Element("Name").ToString();
I am trying to get the elements title and runtime (siblings) where the runtime value is larger than the input value. My C# code with the XPath expression is:
ElementValue = 140;
nodeList = root.SelectNodes(#"/moviedb/movie[./runtime>'" + ElementValue + "'/title | /moviedb/movie[./runtime>'" + ElementValue + "']/runtime");
This XPath expression is not returning anything.
My XML file:
<moviedb>
<movie>
<imdbid>tt0120689</imdbid>
<genres>Crime,Drama,Fantasy,Mystery</genres>
<languages>English,French</languages>
<country>USA</country>
<rating>8.5</rating>
<runtime>189</runtime>
<title lang="english">The Green Mile</title>
<year>1999</year>
</movie>
<movie>
<imdbid>tt0415800</imdbid>
<genres>Action,Animation,Drama,Thriller</genres>
<languages>English</languages>
<country>USA</country>
<rating>4.5</rating>
<runtime>139</runtime>
<title lang="english">Fight Club</title>
<year>2004</year>
</movie>
</moviedb>
You can instead use linq2xml
var doc=XDocument.Load(path);
var movies=doc.Elements("movie")
.Where(x=>(int)x.Element("runtime")>input)
.Select(x=>new
{
Title=x.Element("title").Value,
Runtime=(int)x.Element("runtime")
});
You can now iterate over movies
foreach(var movie in movies)
{
movie.Title;
movie.Runtime;
}
You seem to be applying the values you want off the node as a filter criteria, which won't work. I would go about this another way, first finding the nodes which meet the criteria:
nodeList = root.SelectNodes(#"/moviedb/movie[runtime > " + ElementValue + "]");
And then grabbing the child elements from each:
foreach (var node in nodeList)
{
Debug.WriteLine(node.SelectSingleNode("title").InnerText);
Debug.WriteLine(node.SelectSingleNode("runtime").InnerText);
}
You can do this using a single XPath expression by performing a union i.e. the | operator. As mentioned in other answers here, you had your select inside your predicate which would not result in the correct answer for you anyway.
Note, if you want to see if a number is bigger than another number, unless you are using a Schema driven data-type aware XQuery engine you will need to cast the text() to a number before performing the comparison. In this instance I have assumed an xs:int will be suitable for you. Also you can use the atomic gt as opposed to = which may be more efficient.
ElementValue = 140;
nodeList = root.SelectNodes(#"/moviedb/movie[xs:int(runtime) gt " + ElementValue + "]/(title | runtime)");
I'm trying to work with LINQ to XML to parse the notifications I'm getting from Google Checkout.
The response is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<authorization-amount-notification xmlns="http://checkout.google.com/schema/2" serial-number="153286076708098-00005-6">
<authorization-amount currency="USD">60.0</authorization-amount>
<authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
<avs-response>Y</avs-response>
<cvn-response>M</cvn-response>
<timestamp>2011-06-26T21:28:48.741Z</timestamp>
<google-order-number>153286076708098</google-order-number>
<order-summary>
<total-chargeback-amount currency="USD">0.0</total-chargeback-amount>
<google-order-number>153286076708098</google-order-number>
<total-charge-amount currency="USD">0.0</total-charge-amount>
<total-refund-amount currency="USD">0.0</total-refund-amount>
<risk-information>
<ip-address>77.42.229.34</ip-address>
<billing-address>
<address1>somewhere in Beirut</address1>
<address2></address2>
<phone>70892555</phone>
<email>Technical#fisharwe.com</email>
<contact-name>Fisharwe User</contact-name>
<company-name></company-name>
<fax></fax>
<country-code>LB</country-code>
<city>Beirut</city>
<region></region>
<postal-code>1000</postal-code>
</billing-address>
<avs-response>Y</avs-response>
<cvn-response>M</cvn-response>
<eligible-for-protection>true</eligible-for-protection>
<partial-cc-number>1111</partial-cc-number>
<buyer-account-age>18</buyer-account-age>
</risk-information>
<authorization>
<authorization-amount currency="USD">60.0</authorization-amount>
<authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
</authorization>
<purchase-date>2011-06-26T21:27:48.000Z</purchase-date>
<archived>false</archived>
<shopping-cart>
<items>
<item>
<item-name>Credits</item-name>
<item-description>Description</item-description>
<unit-price currency="USD">60.0</unit-price>
<quantity>1</quantity>
</item>
</items>
</shopping-cart>
<order-adjustment>
<merchant-codes />
<total-tax currency="USD">0.0</total-tax>
<adjustment-total currency="USD">0.0</adjustment-total>
</order-adjustment>
<promotions />
<buyer-id>975104325298289</buyer-id>
<buyer-marketing-preferences>
<email-allowed>false</email-allowed>
</buyer-marketing-preferences>
<buyer-shipping-address>
<address1>somewhere in Beirut</address1>
<address2></address2>
<phone>70892555</phone>
<email>Technical#fisharwe.com</email>
<contact-name>Fisharwe User</contact-name>
<company-name></company-name>
<fax></fax>
<structured-name>
<first-name>Fisharwe</first-name>
<last-name>User</last-name>
</structured-name>
<country-code>LB</country-code>
<city>Beirut</city>
<region></region>
<postal-code>1000</postal-code>
</buyer-shipping-address>
<order-total currency="USD">60.0</order-total>
<fulfillment-order-state>NEW</fulfillment-order-state>
<financial-order-state>CHARGEABLE</financial-order-state>
</order-summary>
</authorization-amount-notification>
Here's the code I'm using:
var serverResponse = _checkoutService.Post(data, GoogleCheckoutConstants.ReportsUri);
var xmlData = XDocument.Parse(serverResponse);
bool charged = false;
if(xmlData.Root.Name.Equals("authorization-amount-notification"))
{
var amount = (from c in xmlData.Elements()
where c.Name.Equals("authorization-amount")
select c).First().Value;
var googleNumber = (from c in xmlData.Elements()
where c.Name.Equals("google-order-number")
select c).First().Value;
_checkoutService.ChargeAndShip(googleNumber, amount);
charged = true;
}
This is the first time I use LINQ to XML, so I'm not really sure what's wrong with my code. But it's not even going inside the if statement. So when I replace the condition with:
if (serverResponse.IndexOf("authorization-amount-notification") > -1)
I end up getting errors telling me that the amount and googleNumber were not found.
Any suggestions?
You need to put the namespace in to the Xml, and you the Elements are SubElements of the Root Node.
You are only after one Element so doing Elements() then .First() is pointless. Just do Element() instead.
Also, you can match element names by passing in the Name of the Element + namespace to the Element() method.
var xmlData = XDocument.Parse(xml);
XNamespace ns = "http://checkout.google.com/schema/2";
if (xmlData.Root.Name == ns + "authorization-amount-notification")
{
var amount =
xmlData
.Root
.Element(ns + "authorization-amount")
.Value;
var googleNumber =
xmlData
.Root
.Element(ns + "google-order-number")
.Value;
_checkoutService.ChargeAndShip(googleNumber, amount);
charged = true;
}
What about...
if(xmlData.Root.Name.LocalName.Equals("new-order-notification")){
....
}
But the xml you posted doesn't seem to match the code your using.. The elements do not exist
I have an XmlDocument object and xml in the format:
<?xml version="1.0" encoding="utf-8"?>
<S xmlns="http://server.com/DAAPI">
<TIMESTAMP>2010-08-16 17:25:45.633</TIMESTAMP>
<MY_GROUP>
<GROUP>1 </GROUP>
<NAME>Amsterdam</NAME>
....
</MY_GROUP>
<MY_GROUP>
<GROUP>2 </GROUP>
<NAME>Ireland</NAME>
....
</MY_GROUP>
<MY_GROUP>
<GROUP>3 </GROUP>
<NAME>UK</NAME>
....
</MY_GROUP>
Using a Lambda expression (or Linq To XML if it's more appropriate) on the XmlDocument object how can i do the following:
get the text of a specific element, say the text of NAME where GROUP = 1
the value of the first occurance of the element "NAME"
Thanks a lot
Assuming you mean XDocument rather than XmlDcoument:
First question:
XNamespace ns = "http://server.com/DAAPI";
string text = (from my_group in doc.Elements(ns + "MY_GROUP")
where (int) my_group.Element(ns + "GROUP") == 1
select (string) my_group.Element(ns + "NAME")).First();
I didn't really understand the second question... what do yuo mean by "contains an element of that name"? Which name? And if you're checking for NAME being equal to a give name, wouldn't you already know that name? Did you perhaps mean the value of GROUP for a specific name? If so, it's easy:
XNamespace ns = "http://server.com/DAAPI";
int group = (from my_group in doc.Elements(ns + "MY_GROUP")
where (string) my_group.Element(ns + "NAME")
select (int) my_group.Element(ns + "GROUP")).First();
Both of these queries assume that the values do exist, and that each MY_GROUP element has a GROUP and NAME subelement. Please let us know if that's not the case.
I have used Linq to XML.
string input = "<?xml version=\"1.0\" encoding=\"utf-8\"?><S xmlns=\"http://server.com/DAAPI\"><TIMESTAMP>2010-08-16 17:25:45.633</TIMESTAMP><MY_GROUP><GROUP>1 </GROUP><NAME>Amsterdam</NAME>....</MY_GROUP><MY_GROUP><GROUP>2 </GROUP><NAME>Ireland</NAME>....</MY_GROUP><MY_GROUP><GROUP>3 </GROUP><NAME>UK</NAME>....</MY_GROUP></S>";
var doc = XDocument.Parse(input);
XNamespace ns = "http://server.com/DAAPI";
//The first question
var name = (from elem in doc.Root.Elements(ns + "MY_GROUP")
where elem.Element(ns + "GROUP") != null //Checks whether the element actually exists - if you KNOW it does then it can be removed
&& (int)elem.Element(ns + "GROUP") == 1 //This could fail if not an integer - insure it is if nessasary
select (string)elem.Element(ns + "NAME")).SingleOrDefault();
I understood only your first question. Here you are for the first:
var xmlSource = myGroup.Load(#"../../MyGroup.xml");
var q = from c in xmlSource.myGroup
where c.group = 1
select c.name;