1

我试图了解 LINQ to XML 功能构造的工作原理。

我有以下示例 XML:

string xml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
                    <People>
                      <Person firstName=""John"" lastName=""Doe"">
                        <ContactDetails>
                          <EmailAddress>john@unknown.com</EmailAddress>
                        </ContactDetails>
                      </Person>
                      <Person firstName=""Jane"" lastName=""Doe"">
                        <ContactDetails>
                          <EmailAddress>jane@unknown.com</EmailAddress>
                          <PhoneNumber>001122334455</PhoneNumber>
                        </ContactDetails>
                      </Person>
                    </People>";

我尝试通过向标签添加一个IsMale属性并添加一个如果它不存在来修改这个 XML。PersonPhoneNumber

我可以通过使用一些程序代码轻松编写此代码:

XElement root = XElement.Parse(xml);

foreach (XElement p in root.Descendants("Person"))
{
    string name =  (string)p.Attribute("firstName") + (string)p.Attribute("lastName");
    p.Add(new XAttribute("IsMale", IsMale(name)));

    XElement contactDetails = p.Element("ContactDetails");

    if (!contactDetails.Descendants("PhoneNumber").Any())
    {
        contactDetails.Add(new XElement("PhoneNumber", "001122334455"));
    }
}

但是MSDN 上的文档说功能构造应该更容易更好地维护。因此,我尝试使用功能构造编写相同的示例。

XElement root = XElement.Parse(xml);

XElement newTree = new XElement("People",
    from p in root.Descendants("Person")
    let name = (string)p.Attribute("firstName") + (string)p.Attribute("lastName")
    let contactDetails = p.Element("ContactDetails")
    select new XElement("Person",
        new XAttribute("IsMale", IsMale(name)),
        p.Attributes(),
        new XElement("ContactDetails",
            contactDetails.Element("EmailAddress"),
            contactDetails.Element("PhoneNumber") ?? new XElement("PhoneNumber", "1122334455")
        )));

可能是我,但我觉得这段代码可读性不好。

如何改进我的功能结构?有没有更好的方法来编写这段代码?

4

3 回答 3

1

正如您引用的 msdn 文章中所述,这取决于您在做什么。如果您对 xml 进行了许多更改,那么非功能性方法将变得复杂且不易理解。在这种情况下会产生什么结果?

foreach (XElement p in doc.Descendants("Person"))
{
    var name = (string)p.Attribute("firstName") + " " + (string)p.Attribute("lastName");
    int age = (int)p.Attribute("age");
    p.RemoveAttributes();
    p.SetAttributeValue("isMale", IsMale(name));
    p.SetAttributeValue("name", name);
    p.SetAttributeValue("age", age);
    p.RemoveNodes();
    p.Name = "Human";
}

我怀疑你一眼就能看出来。这个例子对我来说不是很有描述性。此外,我看不到更改后哪个结构将具有 xml。

XElement people = 
new XElement("People",
    from p in doc.Descendants("Person")
    let name = (string)p.Attribute("firstName") + " " +  (string)p.Attribute("lastName")
    select new XElement("Human",
                    new XAttribute("isMale", IsMale(name)),
                    new XAttribute("name", name),
                    p.Attribute("age")
    )
);

至于我,第二个示例描述了结果并更好地显示了 xml 结构。所以,如果我需要对现有的 xml 做一些小的改动,我会选择非功能性的方法。我会采用功能方法进行大修改。什么是?我认为这是主观的部分。

顺便说一句,结果我只想要age属性左侧和一个name属性而不是名字和姓氏。Person另外,我想要元素而不是Human元素:

<People>
  <Human isMale="false" name="John Doe" age="28" />
  <Human isMale="true" name="Jane Doe" age="27" />
</People>
于 2013-01-18T09:10:55.977 回答
0

您可以通过进一步分解功能来增加代码的表现力,特别是通过尝试隐藏在查询中调用的所有构造函数。我建议几个实用功能:

XElement PersonWithGender(XElement person)
{
    string name = (string)p.Attribute("firstName") + (string)p.Attribute("lastName");
    XAttribute genderAttribute = new XAttribute("isMale", IsMale(name));
    return new XElement(genderAttribute, person.Attributes);
}

XElement PersonWithPhoneNumber(XElement person)
{
    XElement contactDetails = person.Element("ContactDetails");
    XElement emailAddress = contactDetails.Element("EmailAddress");
    XElement phoneNumber = contactDetails.Element("PhoneNumber") ?? new XElement("PhoneNumber", "1122334455");

    return new XElement("ContactDetails", emailAddress, phoneNumber);
}

这些函数每个都将初始 XElement 映射到新改进的 XElement,因此应该非常适合您的查询。如果您将一个结果输入另一个,那么您的查询将变为:

XElement newTree = new XElement("People",
    from p in root.Descendants("Person")
    let pWithGender = PersonWithGender(p)
    select PersonWithPhoneNumber(pWithGender));

我认为这正在接近函数式编程所期望的简洁性和表现力。每个新函数都足够短,可以毫不费力地检查它们,并且查询本身现在更清楚地声明了它的意图。

于 2013-01-18T10:27:28.013 回答
0

Microsoft 使用函数式构造函数的部分建议是首先消除静态 XML 资源。如果您的初始 XML 字符串来自外部源,那么您必须加载、修改并返回它。

但是,如果静态 XML 来自将进行修改的同一代码,并且您的数据可以直接使用,那么您将使用函数式构造函数来创建具有已修改的代码。

Person[] peopleData = new Person[]
{
   new Person("John", "Doe", "john@unknown.com", ""),
   new Person("Jane", "Doe", "jane@unknown.com", "001122334455")
}

XElement people = 
   new XElement("People",
      from p in peopleData
      select new XElement("Human",
                new XAttribute("isMale", p.IsMale),
                new XAttribute("name", p.FullName),
                new XAttribute("age", p.Age)
      )
   );

这种方法几乎与原始 XML 一样易于阅读,但也快得多,因为已经消除了对原始 XML 的解析。

于 2015-06-30T18:47:05.067 回答