I need to group following XML using LINQ to XML or XPath by param/Type. With grouping I would like to get any (or first, doesn't matter) value of id tag.
<list>
<item>
<id>1</id>
<param>
<type>A</type>
</param>
</item>
<item>
<id>2</id>
<param>
<type>A</type>
</param>
</item>
<item>
<id>3</id>
<param>
<type>B</type>
</param>
</item>
<item>
<id>4</id>
<param>
<type>B</type>
</param>
</item>
Desirable results is
A - 1
B - 3
I've tried
var content = from item in doc.Descendants("item").Descendants("param")
group item by new
{
mType = (String)item.Element("type"),
} into g
select new
{
mType = g.Key.mType,
};
but I cannot figure out how to reference ID that is higher in hierarchy , or how to reference PARAM/TYPE when selecting ID.
I would suggest using System.Xml.Linq (XDocument)
If my understanding is good, here is what I've done:
var xml = "<list><item><id>1</id><param><type>A</type></param></item><item><id>2</id><param><type>A</type></param></item><item><id>3</id><param><type>B</type></param></item><item><id>4</id><param><type>B</type></param></item></list>";
var document = XDocument.Parse(xml);
foreach (var param in document.Root.Elements("item").GroupBy(i => i.Element("param").Element("type").Value))
{
var firstId = param.First().Element("id").Value;
Console.WriteLine ("The first of {0} = {1}", param.Key, firstId);
}
the output is :
The first of A = 1
The first of B = 3
In addition to what was suggested by Cedric, you could accomplish the same thing in pure XPath:
var xml = "<list><item><id>1</id><param><type>A</type></param></item><item><id>2</id><param><type>A</type></param></item><item><id>3</id><param><type>B</type></param></item><item><id>4</id><param><type>B</type></param></item></list>";
var document = XDocument.Parse(xml);
// start at the root, then grab the first item element which has a param/type element whose value is equal to 'B'
var answer = document.Root.XPathSelectElement("./item[./param/type='B'][1]").Element("id").Value;
In XPath, the square brackets function effectively like a where clause. My first set of square brackets qualify that I want an item that contains a matching param/type element. The second set of square brackets limit this down to the first match.
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 do not know how to iterate through an XML document to find a node value that belongs to an item that has a specific value in another node. Let me explain more clearly...
Here is my XML:
<Items>
<Item>
<Id>id 1</Id>
<Item>item 1</Item>
<LastModified>2016-01-01</LastModified>
</Item>
<Item>
<Id>id 2</Id>
<Item>item 2</Item>
<LastModified>2016-02-02</LastModified>
</Item>
</Item>
I would like to find the value of <LastModified> where <Id> = id 2 so that I would get the following date: 2016-02-02.
A solution in C# using XDocument would be most appreciated.
Thanks in advance!
You need to find the Item element that contains the Id with your value, and then get its LastModified element:
var lastModified = (DateTime) doc.Descendants("Item")
.Where(x => (string) x.Element("Id") == "id 2")
.Elements("LastModified")
.Single();
See this fiddle for a working example.
I have an XML file, which I like to parse and get the value in a string type array. I know there are XMLSerialization namespace and other things. But what I am trying to achieve is getting the value in a string array. It may be obtained using Foreach loop or for loop.
For example, here is my XML file:
<?xml version="1.0" encoding="utf-8" ?>
<channel>
<title>Social Media</title>
<item>
<title>Facebook</title>
<link>http://www.facebook.com/</link>
</item>
<item>
<title>Twitter</title>
<link>http://www.twitter.com/</link>
</item>
<item>
<title>Google+</title>
<link>http://plus.google.com/</link>
</item>
</channel>
Now, I have two string type array as variable into a C# file.
For example:
public string[] WebsiteName;
public string[] Urls;
Now, I want to get all the values of WebsiteName into the WebsiteName array and website links into the Urls array.
Is there any way to do it? If yes, please show it to me. It will be very helpful.
Here is an example to get website names and links using LINQ:
var xml = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<channel>
<title>Social Media</title>
<item>
<title>Facebook</title>
<link>http://www.facebook.com/</link>
</item>
<item>
<title>Twitter</title>
<link>http://www.twitter.com/</link>
</item>
<item>
<title>Google+</title>
<link>http://plus.google.com/</link>
</item>
</channel>";
var doc = XDocument.Parse(xml);
WebsiteName = doc.Descendants("title").Select(o => o.Value).ToArray();
Urls = doc.Descendants("link").Select(o => o.Value).ToArray();
XDocument.Parse(xml): create XDocument from string. If you want the source is file instead of string then you can use XDocument.Load("path_to_the_xml_file").
doc.Descendants("title"): will get all tags named "title", then
.Select(o => o.Value): will get the string between the opening and closing tag, aka the value
var doc = XDocument.Parse(xml);
var WebsiteName = doc.Descendants("title").Select(o => o.Value).ToArray();
ListBox.ItemsSource = WebsiteName;
And you get content written in listbox.
I am trying to extract those subnodes, but I had got just headache so far...
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<supplyCrew xmlns="http://site.ddf.com">
<login>
<login>XXXX</login>
<password>XXXX</password>
</login>
<flightInformation>
<flights>
<item>
<arrivalDateTime>2010-11-08T22:48:00.000Z</arrivalDateTime>
<arrivingCity>ORD</arrivingCity>
<crewMembers>
<item>
<employeeId>020040</employeeId>
<isDepositor>Y</isDepositor>
<isTransmitter>N</isTransmitter>
</item>
<item>
<employeeId>09000</employeeId>
<isDepositor>N</isDepositor>
<isTransmitter>Y</isTransmitter>
</item>
</crewMembers>
</item>
<item>
<arrivalDateTime>2010-11-08T20:29:00.000Z</arrivalDateTime>
<arrivingCity>JFK</arrivingCity>
<crewMembers>
<item>
<employeeId>0538</employeeId>
<isDepositor>Y</isDepositor>
<isTransmitter>N</isTransmitter>
</item>
<item>
<employeeId>097790</employeeId>
<isDepositor>N</isDepositor>
<isTransmitter>Y</isTransmitter>
</item>
with the code I can get them, but I do not know how to select each one according to their tag name to insert them into a database.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("C:/Crew_Request_Sample.xml");
XmlNodeList elemList = xmlDoc.GetElementsByTagName("item");
foreach (XmlNode node in elemList)
{
Debug.WriteLine(node.InnerText);
}
I need some direction, please.
The problem with using GetElementsByTagName("item") here is that there are 2 levels of item node - one as a child of flights and another item as a child of crewMembers.
Edit Now that the full xml is pasted, it is clear that there is also a namespace involved as well. To handle namespaces, make use of a namespace manager to define aliases for the namespaces, which you can then use in the xpath queries:
var nsm = new XmlNamespaceManager(xmlDoc.NameTable);
nsm.AddNamespace("s", "http://site.ddf.com");
var elemList = xmlDoc.SelectNodes("//s:crewMembers/s:item", nsm);
foreach (var node in elemList)
{
Debug.WriteLine(node.SelectSingleNode("s:employeeId", nsm).InnerText);
Debug.WriteLine(node.SelectSingleNode("s:isDepositor", nsm).InnerText);
Debug.WriteLine(node.SelectSingleNode("s:isTransmitter", nsm).InnerText);
}
You can do it using LINQ2XML..
XElement doc=XElement.Load("C:/Crew_Request_Sample.xml");
XNamespace e = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace s = "http://site.ddf.com";
//this would access the nodes of item->crewMembers->item and put it into an Anonymous Type
var yourList=doc.Descendants(e+"Body")
.Descendants(s+"supplyCrew")
.Descendants(s+"flightInformation")
.Descendants(s+"flights")
.Descendants(s+"item")
.Descendants(s+"crewMembers")
.Descendants(s+"item")
.Select(
x=>new
{
//Anonymous Type
employeeId=x.Element(s+"employeeId").Value,
isDepositor=x.Element(s+"isDepositor").Value,
isTransmitter=x.Element(s+"isTransmitter").Value
}
);
You can then access yourList using for-each loop
foreach(var item in yourList)
{
Console.WriteLine(item.employeeId);
Console.WriteLine(item.isDepositor);
Console.WriteLine(item.isTransmitter);
}
I think you'll do it faster and easily using this technique
Linq To XML
There is a lot of examples in the site, so it 'll be easy to find what you want.
Hope it helps.
Here is the XML I have in a file:
SPECIAL NOTE:
This is a question for Windows Phone 7, not general C#
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<item>
<date>01/01</date>
<word>aberrant</word>
<def>straying from the right or normal way</def>
</item>
<item>
<date>01/02</date>
<word>Zeitgeist</word>
<def>the spirit of the time.</def>
</item>
</rss>
I need it in a List (aka array) of Dictionary objects. Each Dictionary represents an <item>. Each element like <word> is the key with type string and each value like "Zeitgeist" is the value with type string.
Is there any easy way to do this? I'm coming from Objective-C and iOS so this is completely new to me with .NET and C#.
LINQ-to-XML makes it pretty easy. Here's a complete example:
public static void Main(string[] args)
{
string xml = #"
<rss version='2.0'>
<item>
<date>01/01</date>
<word>aberrant</word>
<def>straying from the right or normal way</def>
</item>
<item>
<date>01/02</date>
<word>Zeitgeist</word>
<def>the spirit of the time.</def>
</item>
</rss>";
var xdoc = XDocument.Parse(xml);
var result = xdoc.Root.Elements("item")
.Select(itemElem => itemElem.Elements().ToDictionary(e => e.Name.LocalName, e => e.Value))
.ToList();
}
Instead of loading from a string with XDocument.Parse(), you would probably do XDocument.Load(filename) but either way you get an XDocument object to work with (I did a string just for example).
You can use Linq-Xml to do this:
var doc = XDocument.Parse(xml); //xml is a String with your XML in it.
doc
.Root //Elements under the root element.
.Elements("item") //Select the elements called "item".
.Select( //Projecting each item element to something new.
item => //Selecting each element in the item.
item //And creating a new dictionary using the element name
.Elements() // as the key and element value as the value.
.ToDictionary(xe => xe.Name.LocalName, xe => xe.Value))
.ToList();
Yes, there is an easy way, it's called LINQ to XML.
Some resources:
Parsing complex XML with C#
LINQ to read XML
http://msdn.microsoft.com/en-us/library/bb387098.aspx
Hope this helps...