【问题标题】:How can I bind my collection of objects with complex object attribute?如何将我的对象集合与复杂对象属性绑定?
【发布时间】:2011-11-07 03:37:42
【问题描述】:

我正在尝试加载由 Employee 对象集合组成的 XML 数据。以下函数适用于 String 和 Int 等简单数据类型的属性。我想知道如何导入复杂类型的数据类型。例如,

这个功能很好用:

private void LoadData()
{
   XDocument employeesDoc = XDocument.Load("Employees.xml");
   List<Employee> data = (from employee in employeesDoc.Descendants("Employee")
      select new Employee
      {
         FirstName= employee.Attribute("FirstName").Value,
         LastName = employee.Attribute("LastName ").Value,
         PhoneNumber = employee.Attribute("PhoneNumber").Value
      }).ToList();
  Employees.ItemsSource = data;
}

这是 Employee 类:

public class Employee
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string PhoneNumber { get; set; }
  public Department Department { get; set; }
}

这是部门类:

public class Department
{
  public int Id { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }
  public Employee Manager { get; set; }
}

所以,如果我的 XML 文件看起来像这样:

<Employees>
    <Employee FirstName="John" LastName="Summers" PhoneNumber="703-548-7841" Department="Finance"></Employee>
    <Employee FirstName="Susan" LastName="Hughey" PhoneNumber="549-461-7962" Department="HR"></Employee>

那么,如果 Department 是一个复杂对象并且它是 XML 文件中的一个字符串,我该如何更改我的 LoadData() 函数以将其导入到我的 Employee 对象集合中?

【问题讨论】:

  • 这一切都取决于Department 的序列化方式以及您的XML 是什么样的——这里没有包罗万象的答案。如果Department 仍然只是一个字符串,那么解析与 Linq to XML 无关。
  • @BrokenGlass - 感谢您的回复。从 XML 文件中可以看到,Department 是作为字符串输入的。这些 XML 文件将由非技术人员创建,因此所有内容都将采用字符串格式。因此,鉴于此,我该如何解决这个问题?当我的第一个解决方案是 LINQ to XML 时,为什么这与 LINQ to XML 无关,不是吗?
  • 因为它仅取决于如何解析该字符串并将其映射到您的 Department 对象的属性 - 而字符串解析与 Linq to XML 无关
  • 那么,鉴于上述 XML,我如何将其映射到我的 Employee 类中?这可能吗?

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


【解决方案1】:

您有多种选择,但一切都取决于您如何获取 XML(如何保存)。 (在我看来)最简单的阅读方法是:

XmlSerializer serializer = new XmlSerializer(typeof(YourType));
using (TextReader tr = new StreamReader("newSecret.xml"))
{
 YourType rrr = (YourType)serializer.Deserialize(tr);
}

这里有更多示例: http://msdn.microsoft.com/en-us/library/he66c7f1.aspx

另一方面,如果您(出于某种原因)应该使用 LINQ - 请看这里: LINQ to XML: creating complex anonymous type 和这里 http://blogs.msdn.com/b/xmlteam/archive/2007/03/24/streaming-with-linq-to-xml-part-2.aspx 希望对您有所帮助!

【讨论】:

  • 在这种特殊情况下会有什么帮助?如何使用 XML 反序列化将字符串转换为 Department 对象?
  • 耐心——绝地的主要品质。请参阅我的另一个答案,希望对您有所帮助,因为我不知道您是真正的 xml。无论如何。
【解决方案2】:

如果你能在加载员工之前得到所有部门的列表,你可以将部门放入Dictionary,其中的键是部门名称。然后,您可以为每个员工加载正确的部门:

var departmentsDict = departments.ToDictionary(d => d.Name);

XDocument employeesDoc = XDocument.Load("Employees.xml");
List<Employee> data = (from employee in employeesDoc.Descendants("Employee")
   select new Employee
   {
      FirstName= employee.Attribute("FirstName").Value,
      LastName = employee.Attribute("LastName ").Value,
      PhoneNumber = employee.Attribute("PhoneNumber").Value,
      Department = departmentsDict[employee.Attribute("Department").Value]
   }).ToList();
Employees.ItemsSource = data;

代码可能需要修改,具体取决于您在部门不存在或某人没有指定任何部门时要执行的操作。此代码在这两种情况下都会引发异常。

【讨论】:

    【解决方案3】:
        using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace ConsoleApplication2
    {
    
        public class Employee
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string PhoneNumber { get; set; }
            public Department Department { get; set; }
        }
        public class Department
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public Employee Manager { get; set; }
        }
    
    
        internal class Program
        {
            private static void Main(string[] args)
            {
                String filepath = @"C:\\rrrr.xml";
    
                #region Create Test Data
                List<Employee> list = new List<Employee>();
                for (int i = 0; i < 5; i++)
                {
                    list.Add(new Employee
                                 {
                                     Department = new Department
                                                      {
                                                          Description = "bla bla description " + i,
                                                          Id = i,
                                                          Manager = null,
                                                          Name = "bla bla name " + i
                                                      },
                                     FirstName = "First name " + i,
                                     Id = i + i,
                                     LastName = "Last name " + i,
                                     PhoneNumber = Guid.NewGuid().ToString()
                                 });
                } 
                #endregion
    
                #region Save XML
                XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
                using (Stream fs = new FileStream(filepath, FileMode.Create))
                {
                    using (XmlWriter writer = new XmlTextWriter(fs, Encoding.Unicode))
                    {
                        serializer.Serialize(writer, list);
                    }
                } 
                #endregion
    
    
                //Read from XML
    
                XmlDocument doc = new XmlDocument();
                doc.Load(filepath);
    
                List<Employee> newList = new List<Employee>();
                foreach (XmlNode node in doc.GetElementsByTagName("Employee"))
                {
                    Employee ee = GetEmploee(node);
                    newList.Add(ee);
                }
    
                //ta da
            }
    
            public static Employee GetEmploee(XmlNode node)
            {
                return node == null
                           ? new Employee()
                           : new Employee
                                 {
                                     Department = GetDepartment(node["Department"]),
                                     FirstName = (node["FirstName"]).InnerText,
                                     LastName = (node["LastName"]).InnerText,
                                     Id = Convert.ToInt32((node["Id"]).InnerText),
                                     PhoneNumber = (node["PhoneNumber"]).InnerText
                                 };
            }
    
            public static Department GetDepartment(XmlNode node)
            {
                return node == null
                           ? new Department()
                           : new Department
                                 {
                                     Description = node["Description"].InnerText,
                                     Id = Convert.ToInt32(node["Id"].InnerText),
                                     Manager = GetEmploee(node["Manager"]),
                                     Name = node["Name"].InnerText
                                 };
            }
        }
    }
    

    【讨论】:

    • 这会将结构序列化然后反序列化到 XML 中/从 XML 中反序列化,但与问题中的不同。
    • 我正在使用 XmlSerializer 序列化对象,但我像 XMLDocument 一样读取它们。对不起,对于我的英语,也许我遗漏了一些东西,但你的问题是关于复杂类型的,所以我给你一个答案。或者您只想使用 linq to XML 而不是我的方法?
    • 查看问题中的 XML。每个员工都有Department="Finance",而不是整个Department。而且我不是问这个问题的人。
    • 好的,但他问:“..如果部门是复杂对象,并且是XML文件中的字符串......” 复杂类型不能只是字符串和复杂同时时间。如果您要序列化复杂类型,它将是一个以上的字符串。他进一步说“......我如何更改我的 LoadData() 函数以将其导入到我的 Employee 对象集合中”作者应该使用某种映射将 XmlDoc 转换为他自己的类型......我会很高兴如果我获得更多信息或作者在特定示例下修改了我的代码,请提供帮助。