Parse xml using LINQ to XML to class objects

2019-01-17 23:55发布

问题:

I have

<Model>
    <Components>
        <Component name="a" id="aaa" molarmass="60.05"/>
        <Component name="b" id="bbb" molarmass="18.02"/>
        <Component name="c" id="ccc" molarmass="32.04"/>
        <Component name="d" id="ddd" molarmass="46.03"/>
    </Components>
    ...
</Model>

and the class

public class ChemieComponent
{
    public string Name { get; set; }
    public string Id { get; set; }
    public double MolarMass { get; set; }
}

Can I with the LINQ query parse this components to objects? How? I the end should I have a IEnumerable, right?

EDIT

<Points>
    <Point name="P1" pressure="1">
    <Fractions>
        <Fraction id="aaa" value="0.15272159"/>
        <Fraction id="bbb" value="0.15272159"/>
    </Fractions>
    more points...
 </Points>

回答1:

You can use the following:

XDocument doc = XDocument.Parse(xml);
IEnumerable<ChemieComponent> result = from c in doc.Descendants("Component")
                                      select new ChemieComponent()
                                      {
                                          Name = (string)c.Attribute("name"),
                                          Id = (string)c.Attribute("id"),
                                          MolarMass = (double)c.Attribute("molarmass")
                                      };

EDIT

Accessing nested elements with Linq to Xml is also possible:

public class Point 
{
    public string Name { get; set; }
    public int Pressure { get; set; }

    public IEnumerable<Fraction> Fractions { get; set; }
}

public class Fraction
{
    public string Id { get; set; }
    public double Value { get; set; }
}

static void Main()
{
    string xml = @"<Points>
        <Point name='P1' pressure='1'>
            <Fractions>
                <Fraction id='aaa' value='0.15272159'/>
                <Fraction id='bbb' value='0.15272159'/>
            </Fractions>
        </Point>
        </Points>";

    XDocument doc = XDocument.Parse(xml);
    IEnumerable<Point> result = from c in doc.Descendants("Point")
                                select new Point()
                                {
                                    Name = (string)c.Attribute("name"),
                                    Pressure = (int)c.Attribute("pressure"),
                                    Fractions = from f in c.Descendants("Fraction")
                                                select new Fraction() 
                                                {
                                                    Id = (string)f.Attribute("id"),
                                                    Value = (double)f.Attribute("value"),
                                                }
                                };
}


回答2:

I realise this is an old post, but I've been doing some reading of XML into objects recently. I wasn't keen on Linq to XML - it's not very readable, relies on "magic strings", and requires code changes if the XML schema changes.

For anyone interested, I use the XmlSerializer class to deserialize the XML into objects. Put the deserialization code into a generic helper method, and voila - one-line of code to deserialize an XML file into an object graph:-

using System.IO;
using System.Xml.Serialization;

class Program
{
    static void Main(string[] args)
    {
        var xml = @"<?xml version='1.0' encoding='utf-8' ?>
            <Model>
                <Points>
                    <Point name='P1' pressure='1'>
                        <Fractions>
                            <Fraction id='aaa' value='0.15272159'/>
                            <Fraction id='bbb' value='0.15272159'/>
                        </Fractions>
                    </Point>
                </Points>
            </Model>";

        var model = DeserializeObject<Model>(xml);
    }

    private static T DeserializeObject<T>(string xml)
    {
        var serializer = new XmlSerializer(typeof(T));
        using (var tr = new StringReader(xml))
        {
            return (T)serializer.Deserialize(tr);
        }
    }
}

public class Model
{
    [XmlArrayItem("Point")]
    public Point[] Points { get; set; }
}

public class Point
{
    [XmlAttribute(AttributeName = "name")]
    public string Name { get; set; }

    [XmlAttribute(AttributeName = "pressure")]
    public int Pressure { get; set; }

    [XmlArrayItem("Fraction")]
    public Fraction[] Fractions { get; set; }
}

public class Fraction
{
    [XmlAttribute(AttributeName = "id")]
    public string Id { get; set; }

    [XmlAttribute(AttributeName = "value")]
    public double Value { get; set; }
}

A couple of caveats:-

This solution obviously relies on decorating the domain models with XML attributes, which may or may not be acceptable to you. The solution also relies on an outer element being present in the XML, and won't work if the root element is an array (<Points> in this example).

It's not possible to specify IEnumerable<> (which the OP mentioned); you can use arrays or List<>.