【问题标题】:Parsing XML into classes using LINQ in C#在 C# 中使用 LINQ 将 XML 解析为类
【发布时间】:2015-11-01 18:41:27
【问题描述】:

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

  • 1 个属性
  • 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 示例,他们在开始列出每个建筑物的各个 &lt;Building&gt; 标记之前添加了一个额外的标记,例如 &lt;buildings&gt;。那有必要吗?我看到的 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");

感谢任何帮助,谢谢

【问题讨论】:

  • 反序列化不是更好吗?
  • Q1:格式没有错。您的格式意味着该物业由多个建筑物组成。另一个首先指定该属性由多个建筑物组成,然后列出它们。两者都是正确的。

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


【解决方案1】:

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

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

【讨论】:

  • 很好的答案完美无缺。来吧 OP,接受这个。
【解决方案2】:

您可能需要更改一些内容。

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

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

【讨论】:

  • 感谢您提供有关属性和 XmlAttribute 的信息。我查看了其他几个解决方案,例如 XmlReader,最终选择了 LINQ,主要是因为它似乎是处理 XML 的更新技术标准(我只是想学习如何更好地使用它)
【解决方案3】:

用空列表初始化您的公共字段是非常好的做法,也是避免出现错误的好习惯。如果您不初始化它们,则它们为空,因此会出现错误。

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

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

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

对于 C#

//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()
};

【讨论】:

  • 感谢您提供有关 C# 6 和属性的信息。我必须使用较低的东西,因为 public List&lt;Building&gt; Buildings {get;set;} = new List&lt;Building&gt;(); 没有为我编译。老实说,IEnumerable 的东西让我很困惑,我对那些东西从来都不是很好。我没有很多 C# 经验,还在学习中。
  • IEnumerable 只是一个由许多集合(例如List)实现的接口。它公开了一个枚举器,允许您对其进行迭代(例如,使用 foreach 循环)。在这种情况下,您可以将其替换为 List,然后您必须在 .Select() 方法之后调用 ToList()(我是在调用 Func 之后完成的)。
猜你喜欢
  • 2019-12-28
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
  • 1970-01-01
  • 1970-01-01
  • 2016-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多