tags:

views:

45

answers:

2

I am new to LINQ to XML and need help in mapping an XML hierarchy into my domain object. Here is the XML source:

<?xml version="1.0" encoding="UTF-8"?>
<Listings>
    <Region>United States</Region>
    <Listing>
        <CatID>ELE</CatID>
        <CatDesc>Electronics</CatDesc>
        <ItemID>ELE_LCDTV</ItemID>
        <ItemDesc>LCD TV BLU RAY</ItemDesc>
        <TotalPrice>1500</TotalPrice>
    </Listing>
    <Listing>
        <CatID>COMP</CatID>
        <CatDesc>Computer</CatDesc>
        <ItemID>COMP_LAPTOP</ItemID>
        <ItemDesc>Laptop HP</ItemDesc>
        <TotalPrice>1200</TotalPrice>
    </Listing>
    <Listing>
        <CatID>MISC</CatID>
        <CatDesc>Miscellaneous</CatDesc>
        <ItemID>MISC_WII</ItemID>
        <ItemDesc>Wii</ItemDesc>
        <TotalPrice>350</TotalPrice>
    </Listing>
    <Listing>
        <CatID>COMP</CatID>
        <CatDesc>Computer</CatDesc>
        <ItemID>COMP_HD</ItemID>
        <ItemDesc>Hard Disk</ItemDesc>
        <TotalPrice>300</TotalPrice>
    </Listing>
    <Listing>
        <CatID>ELE</CatID>
        <CatDesc>Electronics</CatDesc>
        <ItemID>ELE_IPOD</ItemID>
        <ItemDesc>iPod</ItemDesc>
        <TotalPrice>225</TotalPrice>
    </Listing>
    <Listing>
        <CatID>COMP</CatID>
        <CatDesc>Computer</CatDesc>
        <ItemID>COMP_WKEY</ItemID>
        <ItemDesc>Wireless Keyboard</ItemDesc>
        <TotalPrice>110</TotalPrice>
    </Listing>
    <Listing>
        <CatID>MISC</CatID>
        <CatDesc>Miscellaneous</CatDesc>
        <ItemID>MISC_GAME</ItemID>
        <ItemDesc>Games</ItemDesc>
        <TotalPrice>50</TotalPrice>
    </Listing>
</Listings>

I have to populate following domain objects ushing above XML. Basically I have to expose an IEnumerable ListCategories()

public class Category
{
    public string ID { get; set; }
    public string Description { get; set; }
    public IList<Item> Items { get; set; } 
}

public class Item
{
    public string ID { get; set; }
    public string Description { get; set; }
    public decimal TotalPrice { get; set; }

}

I undersyand that I have to an orderby query first to sort the XML by CatID and then traverse through it to populate my domain objects.

XDocument xDoc = XDocument.Parse(XmlizedString);

var listing = from x in xDoc.Elements("Listings").Elements("Listing")
                                           orderby (string)x.Element("CatID")
                                           select x;

Above query will sort my XML by CatID but I am not clear how to proceed further...

I would gretly appreciate your suggestions/help in resolving this.

A: 

I do not know Linq-to-xml, however, if the query above iterates through your xml and returns an IEnumerable or similar thing you can populate your objects in following way:

XDocument xDoc = XDocument.Parse(XmlizedString);

var listing = from x in xDoc.Elements("Listings").Elements("Listing")
              orderby (string)x.Element("CatID")
              select new Category {
                  ID = x.CatID, 
                  Description = x.CatDesc, 
                  Items = new Item { 
                      x.ItemID,  
                      Description = x.ItemDesc, 
                      TotalPrice = x.TotalPrice 
                  } 
              };
Muhammad Adeel Zahid
Thanks for your response. Query I gave in my sample only helps me sort (order by) the XML by CatID but I would like to know how best can I populate by domain objects, I mean, Category object listing all categories and their corresponding items.
Alex
+1  A: 

Your Linq-to-xml query would look like this.

var listing = 
xDoc.Elements("Listings")
.Elements("Listing")
.GroupBy (x => new { 
                ID = x.Element(XName.Get("CatID")).Value
                , Description = x.Element(XName.Get("CatDesc")).Value
            }
)
.Select (x => new Category { 
                ID = x.Key.ID
                , Description = x.Key.Description
                , Items = x.Select (i => new Item { 
                                    ID = i.Element("ItemID").Value
                                    , Description = i.Element("ItemDesc").Value
                                    , TotalPrice = decimal.Parse(i.Element("TotalPrice").Value) 
                                   }
                    ).ToList()
            }
        )
.OrderBy (x => x.ID);
MarkisT
Thanks much MarkisT, your solution works like a charm. For some of the items, TotalPrice element might be missing from XML if there is no price and I am getting an exception for that. Is there a way to set 0.00 value for TotalPrice in case "TotalPrice" element is missing from XML?
Alex
coversly u can declare TotalPrice quality as nullable
Muhammad Adeel Zahid
Even if I try to make TotalPrice as nullable in my class I am getting an exception because exception is happing at following line of code i.Element("TotalPrice").Value as TotalPrice element itself is missing from source XML.
Alex
Leave the TotalPrice as nullable and then when you set TotalPrice in the select statement use this instead:TotalPrice = i.Element("TotalPrice") != null ? decimal.Parse(i.Element("TotalPrice").Value) : null
MarkisT
Thanks MarkisT. Sorry I don't want to be a pain but I tried your suggestion but getting a compile time error. I made TotalPrice as nullable in my class like this - public decimal? TotalPrice { get; set; } and I replaced your line of code - TotalPrice = i.Element("TotalPrice") != null ? decimal.Parse(i.Element("TotalPrice").Value):null AND I get following error: Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between 'decimal' and '<null>'
Alex
I resolved it by using following line of code: TotalPrice = decimal.Parse(i.Elements("TotalPrice").Any() ? i.Element("TotalPrice").Value : "0"). I hope I am correct. Thank you all.
Alex
That was the next thing I was going to suggest. Glad you figured it out!
MarkisT