使用LINQ to XML解析XML到类对象

13

我有

<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>
还有这个类。
public class ChemieComponent
{
    public string Name { get; set; }
    public string Id { get; set; }
    public double MolarMass { get; set; }
}

我能用LINQ查询将这些组件解析为对象吗?如何操作?最终,我应该拥有一个IEnumerable,对吗?

编辑

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

26

您可以使用以下代码:

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")
                                      };

编辑

使用 Linq to Xml 访问嵌套元素也是可能的:

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"),
                                                }
                                };
}

最好检查是否为空,否则你的应用程序会崩溃。 - Only Bolivian Here
@Wouter:谢谢!那个很好用。我能做子查询吗?还是必须得做两个查询?请看我的编辑。 - Pedro Dusso

4
我知道这是一篇旧文章,但我最近一直在阅读关于将XML转化为对象的内容。我不太喜欢使用Linq to XML - 它不是很易读,依赖于"magic strings",并且如果XML架构发生变化就需要改变代码。
对于任何感兴趣的人,我使用XmlSerializer类将XML反序列化为对象。将反序列化代码放入通用的帮助方法中,然后就可以一行代码将XML文件反序列化为对象图形了:
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; }
}

需要注意的几点:

这个解决方案显然依赖于使用XML属性装饰领域模型,这可能或可能不被您接受。此解决方案还依赖于XML中存在外部元素,如果根元素是数组(例如<Points>),则无法使用。

无法指定IEnumerable<>(OP提到了这一点);您可以使用数组或List<>


7年太晚了,但是“如果XML架构更改,则需要代码更改”对于这个解决方案也是正确的。 - Fawad Raza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接