Search code examples
c#xmllinqparsing

Parsing XML Data with LINQ


I am new to LINQ. I need to return the id with the correct price information for today's date for each MPrice.

Here is an example of the XML:

<Pricing>
<MPrice>
    <Id>0079</Id>
      <Price>
        <Price>31.25</Price>
        <StartDt>2009-8-01</StartDt>
        <EndDt>2009-08-26</EndDt>
      </Price>
      <Price>
        <ListPrice>131.25</ListPrice>
        <StartDt>2009-08-26</StartDt>
        <EndDt>9999-12-31</EndDt>
       </Price>
   </MPrice>
   <MPrice>
    <Id>0081</Id>
      <Price>
        <Price>131.25</Price>
        <StartDt>2009-8-01</StartDt>
        <EndDt>2009-08-26</EndDt>
      </Price>
      <Price>
        <ListPrice>231.25</ListPrice>
        <StartDt>2009-08-26</StartDt>
        <EndDt>9999-12-31</EndDt>
       </Price>
   </MPrice> 
</Pricing>

Solution

  • Here is one way of doing it:

    using System;
    using System.Linq;
    using System.Xml.Linq;
    
    class Program
    {
        static void Main()
        {
            String xml = @"<Pricing>
                <MPrice>
                    <Id>0079</Id>
                    <Price>
                    <Price>31.25</Price>
                    <StartDt>2009-8-01</StartDt>
                    <EndDt>2009-08-26</EndDt>
                    </Price>
                    <Price>
                    <ListPrice>131.25</ListPrice>
                    <StartDt>2009-08-26</StartDt>
                    <EndDt>9999-12-31</EndDt>
                    </Price>
                </MPrice>
               </Pricing>";
    
            var priceInfo = from e in XElement.Parse(xml).Elements("MPrice").Elements("Price")
                    let start = DateTime.Parse(e.Descendants("StartDt").FirstOrDefault().Value)
                    let end = DateTime.Parse(e.Descendants("EndDt").FirstOrDefault().Value)
                    where start < DateTime.Now && end > DateTime.Now
                    select new { Id = e.Parent.Element("Id").Value, ListPrice = e.Element("ListPrice").Value };
    
            Console.WriteLine(priceInfo.FirstOrDefault().Id);
            Console.WriteLine(priceInfo.FirstOrDefault().ListPrice);
        }
    }
    

    Output:

    0079
    131.25
    

    Please note that there needs to be much more error checking than this example provides. I would specifically add checking around the parsing of the datetime (perhaps by using a function that wraps DateTime.TryParseExact).

    Edit: If you want to use an XDocument instead of an XElement you will need to make a subtle change to the query (notice the usage of the Descendants method instead of the Elements method):

    var priceInfo = from e in XDocument.Parse(xml).Descendants("MPrice").Elements("Price")
            let start = DateTime.Parse(e.Descendants("StartDt").FirstOrDefault().Value)
            let end = DateTime.Parse(e.Descendants("EndDt").FirstOrDefault().Value)
            where start < DateTime.Now && end > DateTime.Now
            select new { Id = e.Parent.Element("Id").Value, ListPrice = e.Element("ListPrice").Value };
    

    Remember that you don't need to use an XDocument unless you are working with the XML as a true document. In most cases, the XElement type is sufficient.

    Edit #2: If you want to load the XDocument from disk then use this approach:

    using System;
    using System.Linq;
    using System.Xml.Linq;
    
    class Program
    {
        static void Main()
        {
            XDocument document = XDocument.Load(@"d:\test.xml");
    
            var priceInfo = from e in document.Descendants("MPrice").Elements("Price")
                    let start = DateTime.Parse(e.Descendants("StartDt").FirstOrDefault().Value)
                    let end = DateTime.Parse(e.Descendants("EndDt").FirstOrDefault().Value)
                    where start < DateTime.Now && end > DateTime.Now
                    select new { Id = e.Parent.Element("Id").Value, ListPrice = e.Element("ListPrice").Value };
    
            Console.WriteLine(priceInfo.FirstOrDefault().Id);
            Console.WriteLine(priceInfo.FirstOrDefault().ListPrice);
        }
    }