如何将 ISortedEnumerable<XElement> 添加到 XElement?

How can I add ISortedEnumerable<XElement> to XElement?

我正在尝试使用 Linq 对 XElement 的子项进行排序,然后将现有的子项替换为已排序。

首先我创建 XElement:

XElement WithLinq =
            new XElement("Names",
                from cust in Customers.AsEnumerable()
                select
                    new XElement("Customer",
                        new XAttribute("ID", cust.ID),
                        new XElement("Name", cust.Name),
                        new XElement("Purchases",
                        from pur in cust.Purchases
                        select
                            new XElement("Purchase",
                                new XElement("Produkt",pur.Description),
                                new XAttribute("ID",pur.ID),
                                new XElement("Price",pur.Price),
                                new XComment("teraz daty"),
                                new XElement("Date",pur.Date), //Formatuje DateTime zgodnie z normami XMLa
                                new XElement("DataAleNieDoKonca",pur.Date.ToString(CultureInfo.InvariantCulture)))))    
                        );

然后我对节点进行排序:

var NowaKolejnosc = WithLinq.Elements().Last().Elements().OrderBy(n => n.Name).ThenBy(n => n.Value);

并替换它们:

WithLinq.Elements().Last().ReplaceNodes(NowaKolejnosc);

但是我得到一个运行时异常:ArgumentException: 'Co najmniej jeden obiekt musi implementować element IComparable.' 翻译:至少一个对象必须实现 IComparable。

我不明白导致异常的原因以及如何解决它。

发生错误是因为 XElement.Name 的类型是 System.Xml.Linq.XNameXName 没有实现 IComparable.

XName 包装了一个 System.String 值,并将 ToString 覆盖为 return System.String 值。

由于 System.String 实现了 IComparable,我们可以利用这些知识正确并成功地调用 OrderBy。这具有所需的语义,因为从逻辑上讲,我们想要比较包装的字符串。

WithLinq.Elements().Last().Elements().OrderBy(n => n.Name.ToString()).ThenBy(n => n.Value)

当使用多个排序 LINQ 运算符时,我发现使用查询表达式语法更具可读性。

from element in WithLinq.Elements().Last().Elements()
orderby element.Name.ToString(), element.Value
select element

这是基于 Aluan Haddad 接受的答案的评论。

建议:考虑用XName.LocalName代替XName.ToString()

直接使用 element.Name.LocalName 属性 可能是合适的,前提是 XML 不使用名称空间 XML 不需要特定操作。

在处理大型 (> 1GB) XML 文件时,我发现通过将 XName.ToString() 换成 XName.LocalName 可以获得适度的性能提升。

尽管有传言,这一更改在需要重复排序和比较的 1 小时长程序中节省了大约六分钟。在其他情况下,YMMV。

对于某些上下文,以下是 the reference source 的区别:

/// <summary>
/// Returns the expanded XML name in the format: {namespaceName}localName.
/// </summary>
public override string ToString() {
    if (ns.NamespaceName.Length == 0) return localName;
    return "{" + ns.NamespaceName + "}" + localName;
}
/// <summary>
/// Gets the local (unqualified) part of the name.
/// </summary>
/// <seealso cref="XName.Namespace"/>
public string LocalName {
    get { return localName; }
}