在C#中使用LINQ将XML解析为类

时间:2015-11-01 18:41:28

标签: c# xml linq linq-to-xml

我在将XML文档解析为自定义类时遇到了很多麻烦。我试着阅读我在网上和这里可以找到的东西,但我仍然没有到达任何地方。我正在开发一个房地产应用程序,并且我正在尝试为您拥有的基本属性建模:

  • 1 property
  • 1个属性可以有多个建筑物
  • 每栋建筑可以有多个租户。

我决定尝试将数据存储在xml文档中,我做了如下示例:

<?xml version="1.0" encoding="UTF-8"?>
<Property>
    <Name>Grove Center</Name>
    <Building>
        <Name>Building1</Name>
        <Tenant>
            <Name>Tenant1</Name>
            <SquareFeet>2300</SquareFeet>
            <Rent>34000</Rent>
        </Tenant>
        <Tenant>
            <Name>Tenant2</Name>
            <SquareFeet>3100</SquareFeet>
            <Rent>42000</Rent>
        </Tenant>
        <Tenant>
            <Name>Tenant3</Name>
            <SquareFeet>1700</SquareFeet>
            <Rent>29000</Rent>
        </Tenant>
    </Building>
    <Building>
        <Name>Building2</Name>
        <Tenant>
            <Name>Tenant1</Name>
            <SquareFeet>6150</SquareFeet>
            <Rent>80000</Rent>
        </Tenant>
        <Tenant>
            <Name>Tenant2</Name>
            <SquareFeet>4763</SquareFeet>
            <Rent>60000</Rent>
        </Tenant>
    </Building>
</Property>

实际上我的第一个问题是这个格式是否正确..我看到一些xml示例,他们在开始列出每个建筑物的单个<buildings>标签之前添加了额外的标签,例如<Building> 。这有必要吗?我看到的W3C示例并没有这样做..但是这个关于stackexchange的帖子非常接近我正在做的事情:Parsing XML with Linq with multiple descendants

以下是我在C#中的类的代码:

public class Property
{
    public string Name { get; set; }
    public List<Building> Buildings = new List<Building>();
}

public class Building
{
    public string Name { get; set; }
    public List<Tenant> Tenants = new List<Tenant>();
}

public class Tenant
{
    public string Name { get; set; }
    public int SF { get; set; }
    public decimal Rent { get; set; }
}

我不确定在类定义中是否在我的列表中使用new关键字是一种很好的做法..但是我在程序中稍后尝试将一个建筑物或租户添加到列表中时遇到错误,所以我没有'我知道还能做什么。现在,我的主要代码并没有比:

更进一步
Property p = new Property();
XDocument doc = XDocument.Load(@"C:\Users\SampleUser\Desktop\sample-property.xml");

感谢任何帮助,谢谢

3 个答案:

答案 0 :(得分:2)

以下查询将为您提供正确的结果: -

Property p = new Property
               {
                  Name = (string)doc.Root.Element("Name"),
                  Buildings = doc.Root.Elements("Building")
                                 .Select(x => new Building
                                  {
                                     Name = (string)x.Element("Name"),
                                     Tenants = x.Elements("Tenant")
                                                .Select(t => new Tenant
                                                 {
                                                     Name = (string)t.Element("Name"),
                                                     SF = (int)t.Element("SquareFeet"),
                                                     Rent = (decimal)t.Element("Rent")
                                                 }).ToList()
                                   }).ToList()
                };

答案 1 :(得分:0)

你可能想要改变一些事情。

属性名称必须与xml标记匹配,或者您必须手动指定映射。在您的示例代码中,Buildings和Tenants声明为字段,您应该将其更改为属性。如果需要,可以将它们初始化为构造函数中的空列表:

public class Property
{
    public string Name { get; set; }
    [XmlElement("Building")]
    public List<Building> Buildings { get; set; }

    public Property()
    {
        Buildings = new List<Building>();
    }
}

public class Building
{
    public string Name { get; set; }
    [XmlElement("Tenant")]
    public List<Tenant> Tenants { get; set; }

    public Building()
    {
        Tenants = new List<Tenant>();
    }
}

public class Tenant
{
    public string Name { get; set; }
    [XmlAttribute("SquareFeet")]
    public int SF { get; set; }
    public decimal Rent { get; set; }
}

此外,我建议反序列化文件而不是使用linq。考虑这些辅助方法:

public static class XmlHelper
{
    public static T DeserializeFromXmlString<T>(string xml)
    {
        var xmlSerializer = new XmlSerializer(typeof (T));
        using (var stringReader = new StringReader(xml))
        {
            return (T) xmlSerializer.Deserialize(stringReader);
        }
    }

    public static T DeserializeFromXmlFile<T>(string filename) where T : new()
    {
        return DeserializeFromXmlString<T>(File.ReadAllText(filename));
    }
}

反序列化很容易:

var listOfProperties = XmlHelper.DeserializeFromXmlFile<Property>(@"C:\Users\SampleUser\Desktop\sample-property.xml");

答案 2 :(得分:0)

使用空列表初始化您的公共字段是完全正确和良好的做法,以避免您得到的错误。如果你没有初始化它们,它们就是null,因此是错误。

但是,您可以使用属性而不是列表中的字段。

从C#6开始,您可以使用简化的自动属性分配:

public List<Building> Buildings {get;set;} = new List<Building>();

对于C#&lt; 6您可以使用自动属性并在构造函数中初始化属性,或使用带有支持字段的属性。

//Auto property with assignment in constructor
public class Property
{
    public string Name { get; set; }
    public List<Building> Buildings {get;set;};
    public Property(){
        Buildings = new List<Building>();
    }
}

//Property with backing field
public class Property
{
    private List<Building> _buildings = new List<Building>();
    public string Name { get; set; }
    public List<Building> Buildings {get {return _buildings;} set {_buildings = value;}};
}

要读取XML并创建对象图,可以将LINQ与对象初始化器结合使用。

Func<IEnumerable<XElement>, IEnumerable<Tenant>> getTenants = elements => {
    return elements.Select (e => new Tenant {
        Name = e.Element("Name").Value,
        Rent = decimal.Parse(e.Element("Rent").Value),
        SF = int.Parse(e.Element("SquareFeet").Value)
    });
};

Func<IEnumerable<XElement>, IEnumerable<Building>> getBuildings = elements => {
    return elements.Select (e => new Building{
        Name = e.Element("Name").Value,
        Tenants = getTenants(e.Elements("Tenant")).ToList()
    });
};

//xdoc is your parsed XML document
//e.g. var xdoc = XDdocument.Parse("xml contents here");
var property = new Property{
    Name = xdoc.Root.Element("Name").Value,
    Buildings = getBuildings(xdoc.Root.Elements("Building")).ToList()
};