简体   繁体   中英

Parsing XML with key value pair in C# using linq

I have the following XML

<?xml version="1.0"?>
<NavResponse>
<Response>
<Products>
<Product>
<field name="Id" value="BST-U3009W"/>
<field name="Point" value="1"/>
</Product>
<Product>
<field name="Id" value="BST-U5047W"/>
<field name="Point" value="1"/>
</Product>
<Product>
<field name="Id" value="BST-U7023W"/>
<field name="Point" value="1"/>
</Product>
<Product>
<field name="Id" value="BST-U9007"/>
<field name="Point" value="1"/>
</Product>
<Product>
<field name="Id" value="BTS-U8010"/>
<field name="Point" value="1"/>
</Product>
</Products>
</Response>
<Id>00000000-0000-0000-0000-000000000000</Id>
<Errors/>
</NavResponse>

I am trying to turn it into a list of product

where product is

public class Product
{
    public string Id {get;set;}
    public int Point {get;set;}
}

I tried using Linq this way

XDocument fooXML = XDocument.Load(@"c:\fred\foo.xml");

and then this

var pairs = XDocument.Parse(fooXML.ToString())
                .Descendants("field")
                .Select (xd => new {Key = xd.Attribute("name").Value,
                                    Value = xd.Attribute("value").Value}).ToList();

but I get this a list of 10 records.

Id BST-U3009W 
Point 1 
Id BST-U5047W 
Point 1 
Id BST-U7023W 
Point 1 
Id BST-U9007 
Point 1 
Id BTS-U8010 
Point 1 

When I want a list of 5 records like this

Id = BST-U3009W , Point = 1 
Id = BST-U5047W , Point = 1 
Id = BST-U7023W,  Point = 1 
Id = BST-U9007,   Point = 1 
Id = BTS-U8010,   Point = 1 

I know I could probably cheat my way by using 2 loops and building the list manually, but that I would much rather learn how to do it properly.

Thanks

You should query for Product elements first and then get field s for every one separately:

var query = from p in xDoc.Root.Element("Response")
                               .Element("Products")
                               .Elements("Product")
            let id = p.Elements("field")
                      .First(x => (string)x.Attribute("name") == "Id")
            let point = p.Elements("field")
                         .First(x => (string)x.Attribute("name") == "Point")
            select new Product {
                Id = (string)id.Attribute("value"),
                Point = (int)point.Attribute("value")
            };

var items = query.ToList();

If you wanted to go one step further and make sure that each product you select has both an Id field and a point field and also that the point will parse to an int - you could do something like this.

XDocument xml = XDocument.Load(@"c:\fred\foo.xml");
int i = default(int);
var pairs = from product in xml.Descendants("Product")
            let id = product.Elements("field").FirstOrDefault(f => f.Attribute("name") != null && f.Attribute("value") != null && f.Attribute("name").Value == "Id")
            let point = product.Elements("field").FirstOrDefault(f => f.Attribute("name") != null && f.Attribute("value") != null && f.Attribute("name").Value == "Point" && int.TryParse(f.Attribute("value").Value, out i))
            where id != null && point != null
            select new Product { Id = id.Attribute("value").Value, Point = i };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM