Parsing Parent Nodes of an XML File Usinq LINQ to XML
Recently, a reader submitted an interesting problem to me. He had an XML file which he was trying to parse into a class, but he was having difficulty populating the class with both the selected results and their parent's values.
After a little experimentation, I realized you can call the Parent property of an XDocument queried item. Parent will return the XElement node that is the parent node of the current item.
In order for LINQ to XML to work most efficiently, you need to open the XML document using the XDocument class:
XDocument xdoc =
XDocument.Load(@"c:temp2file.xml");
This pre-parses the document into a collection of nodes (XElements). You can then use LINQ to XML to query on the nodes, elements, and attributes of the file.
Notice in the example, I am calling ObjectDumper to output the results of the query. ObjectDumper was a utility class provided in the LINQ Preview. You can download a copy of ObjectDumper here – ObjectDumper.cs (5.19 kb)
The reader sent me the following XML file he was trying to parse:
Example XML File (file.xml):
<Authorizations>
<Auth>
<AuthNumber>AA12345</AuthNumber>
<Lines>
<Line>
<SerNum>123456</SerNum>
<Qty>1</Qty>
<ShipDate>2008-05-15</ShipDate>
<Carrier>
<CarrierID>2</CarrierID>
<CarrierShipDate>2008-05-17</CarrierShipDate>
</Carrier>
<Comments ischg="N">
<CommentText>item shipped</CommentText>
<CommentDTM>2008-05-17</CommentDTM>
</Comments>
</Line>
<Line>
<SerNum>789123</SerNum>
<Qty>8</Qty>
<ShipDate>2008-05-15</ShipDate>
<Carrier>
<CarrierID>9</CarrierID>
<CarrierShipDate>2008-05-15</CarrierShipDate>
</Carrier>
<Comments ischg="Y">
<CommentText>item shipped</CommentText>
<CommentDTM>2008-05-15</CommentDTM>
</Comments>
</Line>
</Lines>
</Auth>
</Authorizations>
His goal was to create a List<> class to hold the "Line" nodes. The class should also store the "AuthNumber". For our query to return all the "Line" nodes, we need to query on "Line". However, doing this will essentially skip the "AuthNumber" nodes (in his production app, there are multiple "AuthNumber" nodes, not just one as in our example). My solution was to query on "Line", but use the Parent property of each returned node to go up the tree to the parent (in this case, "Auth"). Each query result is converted into a ShipItem class and added to the return list.
LINQ to XML Example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;namespace ConsoleApplication1_xml
{
class ShipItem
{
public string AuthNumber { get; set; }
public string SerNum { get; set; }
public int Qty { get; set; }
public DateTime ShipDate { get; set; }
public string CarrierID { get; set; }
public DateTime CarrierShipDate { get; set; }
public bool CommentsChanged { get; set; }
public string CommentText { get; set; }
public DateTime CommentDTM { get; set; }
}class Program
{
static void Main(string[] args)
{
// load the file as an xdocument
XDocument xdoc = XDocument.Load(@"c:temp2file.xml");// get the items
List<ShipItem> ItemList = (from xml in xdoc.Elements("Authorizations").Elements("Auth").Elements("Lines").Elements("Line")
select new ShipItem()
{
AuthNumber = xml.Parent.Parent.Element("AuthNumber").Value,
SerNum = xml.Element("SerNum").Value,
Qty = Convert.ToInt32(xml.Element("Qty").Value),
ShipDate = Convert.ToDateTime(xml.Element("ShipDate").Value),
CarrierID = xml.Element("Carrier").Element("CarrierID").Value,
CarrierShipDate = Convert.ToDateTime(xml.Element("Carrier").Element("CarrierShipDate").Value),
CommentsChanged = xml.Element("Comments").Attribute("ischg").Value == "Y",
CommentText = xml.Element("Comments").Element("CommentText").Value,
CommentDTM = Convert.ToDateTime(xml.Element("Comments").Element("CommentDTM").Value)
}).ToList();// output result
ObjectDumper.Write(ItemList);// wait
Console.ReadLine();
}
}
}
If you have any questions, comments, or suggestions, please feel to post them in the LINQ Exchange Forum
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
