在 C# 中使用 Linq to XML 在文档中搜索不同的 XML 结构

Posted

技术标签:

【中文标题】在 C# 中使用 Linq to XML 在文档中搜索不同的 XML 结构【英文标题】:Searching for distinct XML structures in a document with Linq to XML in C# 【发布时间】:2019-11-03 17:27:13 【问题描述】:

我编写了一个小 C# 来解析 XML 文档中的许多元素并只返回那些具有不同子结构的元素中的第一个?例如,如果我有以下 XML 文档,那么对 rootElement.DistinctStructures("base") 的调用将返回一个 IEnumerable<XElement>,其中仅包含 ID 为 1、3 和 5 的基本元素。

<root>
    <base id="1">
        <a>text</a>
    </base>
    <base id="2">
        <a>more text</a>
    </base>
    <base id="3">
        <b>text</b>
    </base>
    <base id="4">
        <a>other text</a>
    </base>
    <base id="5">
        <a>
            <c>sub text</c>
        </a>
    </base>
</root>

基本代码从结构中的元素名称和文本节点生成唯一键,并将它们与保存的唯一元素集合进行比较。我的问题是是否有更简洁的方法来做到这一点?

private Dictionary<string, XElement> uniqueElements = new Dictionary<string, XElement>();

public void Go()

    foreach (var entry in xmlDoc.Elements("e"))
    
        string keyString = AsStructureString(entry).ToString();
        if (!uniqueElements.Keys.Contains(keyString))
        
            uniqueElements.Add(keyString, entry);
        
    


public StringBuilder AsStructureString(this XElement input)

    StringBuilder sb = new StringBuilder(input.Name.LocalName);

    var NodesOfNote = input.Nodes().Where(n => n.NodeType == XmlNodeType.Element || n.NodeType == XmlNodeType.Text).ToList();

    if (NodesOfNote.Any())
    
        sb.Append(">>");
    

    foreach (var childNode in NodesOfNote)
    
        if (childNode.NodeType == XmlNodeType.Element)
        
            sb.Append((childNode as XElement).AsStructureString());
        
        if (childNode.NodeType == XmlNodeType.Text)
        
            sb.Append("txt");
        
        if (!childNode.IsLastIn(NodesOfNote))
        
            sb.Append("|");
        
    

    return sb;

【问题讨论】:

贴出你试过的代码。 已添加代码。希望这会有所帮助。 【参考方案1】:

这可能比您想象的要容易。如果决定节点结构的是其元素和文本(无论内容如何),您可以这样做:

IEnumerable<XElement> DistinctStructures(XContainer root, XName name)

    return
        from d in root.Descendants(name)
        group d by GetKey(d) into g
        select g.First();

    string GetKey(XElement n) =>
        String.Join(",",
            n.DescendantNodes().Select(d =>
                d is XElement e ? $"e.Name^GetDepth(e)"
                : d is XText t ? $"<text>^GetDepth(t)"
                : default
            )
        );
    int GetDepth(XObject o)
    
        var depth = 0;
        for (var c = o; c != null; c = c.Parent)
            ++depth;
        return depth;
    

【讨论】:

谢谢。这绝对是更清洁和更多的 LINQy。解决方案需要注意的一件事是 GetKey 中的默认文字要求您使用 C# 7.1。但是用 string.Empty 替换很容易。

以上是关于在 C# 中使用 Linq to XML 在文档中搜索不同的 XML 结构的主要内容,如果未能解决你的问题,请参考以下文章

C#操作xml文档增删改查(Linq to XML)

在 C# 中使用 LINQ-To-XML 解析具有多个列表和类对象的 XML 数据

LINQ to XML:如何在 C# 中获取元素值?

Linq to Xml C#在特定元素中查找特定元素

C#中的Linq to Xml详解

C#中的Linq to Xml详解