XDocument find node value where other node value equals my search - c#

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.

Related

C# XML find first Element descendants

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

Grouping in LINQ to XML with "any" or first" aggregation

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.

Sort file by Element value [duplicate]

This question already has an answer here:
How to sort XML in LINQ C# by an attribute value? Also MVC
(1 answer)
Closed 7 years ago.
I have the following xml file.(sample) .. I need to sort the 'invoice' nodes by the attribute 'InvcDate'. Is this even possible in Linq? Any help would be much appreciated.
I have been trying for some time however I don't have much experience with xml and and am a relative newcomer to programming, so I would be very grateful for any help at all.
<?xml version="1.0" encoding="utf-8"?>
<Server>
<Name>AlignServer</Name>
<Params>
<marketNo>MT</marketNo>
<dateFrom>2015-01-06</dateFrom>
<dateTo>2015-01-09</dateTo>
<Sales>
<invoices>
<invoice>
<header>
<InvoiceNum>22947</InvoiceNum>
<InvcDate>2015/01/07-110104</InvcDate>
</header>
<item>
<SKU>6595456987453</SKU>
<Qty>-1</Qty>
</item>
</invoice>
<invoice>
<header>
<InvoiceNum>23056</InvoiceNum>
<InvcDate>2015/01/08-020627</InvcDate>
</header>
<item>
<SKU>9845256242255</SKU>
<Qty>-1</Qty>
</item>
</invoice>
<invoice>
<header>
<InvoiceNum>22899</InvoiceNum>
<InvcDate>2015/01/06-094505</InvcDate>
</header>
<item>
<SKU>5454256565452</SKU>
<Qty>-1</Qty>
</item>
<item>
<SKU>11111165454130</SKU>
<Qty>4</Qty>
</item>
</invoice>
</invoices>
</Sales>
</Params>
</Server>
I have tried
XElement root = XElement.Load("C:\\xmlsort\\test.xml");
XElement[] sortedTables = root.Elements("invoices").OrderBy(t => (Datetime)t.Element("invdate")).ToArray();
root.ReplaceAll(sortedTables);
root.Save("C:\\xmlsort\\test.xml");
What I have done so far - with suggestion from #ec8or and seems to work but still open to suggestions:
XElement root = XElement.Load("C:\\xmlsort\\test.xml");
var invoices = from p in root.Descendants("invoice")
orderby DateTime.ParseExact(p.Element("header").Element("InvcDate").Value, "yyyy/MM/dd-hhmmss", CultureInfo.InvariantCulture)
select p;
XElement[] sortedTables = invoices.ToArray();
root.ReplaceAll(sortedTables);
root.Save("C:\\xmlsort\\output.xml");
Read you XML in a XElement:
XElement element = XElement.Load("doc.xml");
Query you XML data:
var invoices = from p in element.Descendants ("invoice")
orderby DateTime.ParseExact(p.Element("header").Element("InvcDate").Value, "yyyy/MM/dd-hhmmss", CultureInfo.InvariantCulture)
select p;
Print it to the console:
foreach (var invoice in invoices) {
Console.WriteLine (invoice.ToString ());
}
EDIT
Answer to your question in comments.
XDocument doc = XDocument.Load ("data.xml");
Select all parent node:
var baseElement = doc.XPathSelectElement ("/Server/Params/Sales/invoices");
sort the inner nodes:
var sortedElements = baseElement.Elements ()
.OrderBy (e => (DateTime)e.XPathSelectElement("header/InvoiceNum"))
.ToList ();
replace the current content with the sortet content:
baseElement.ReplaceAll (sortedElements);
doc.Save ("out.xml");

Parse XML into String type array in Windows Phone 8 App using C#

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.

How do I put XML into a List of Dictionaries with C# on Windows Phone 7

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...

Categories