Поиск различных XML-структур в документе с Linq to XML в C # - PullRequest
0 голосов
/ 20 июня 2019

Я написал небольшой C # для анализа ряда элементов в XML-документе и возврата только первого из тех элементов, которые имеют отличную дочернюю структуру? Например, если у меня есть следующий документ XML, то вызов rootElement.DistinctStructures("base") скажем, возвращает IEnumerable<XElement>, содержащий только базовые элементы с идентификаторами 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 Ответ

1 голос
/ 25 июня 2019

Это может быть проще, чем вы думаете. Если то, что определяет структуру узла, это его элементы и текст (независимо от содержимого), вы можете сделать это:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...