Как удалить повторяющиеся недействительные дочерние узлы из XML-документа с помощью Linq to XML?

Я создаю XML из JSON, полученного из вызова HttpWebRequest, с помощью JsonConvert. JSON, который я возвращаю, иногда имеет повторяющиеся узлы, что создает повторяющиеся узлы в XML после преобразования, которые затем мне нужно удалить.

Обработка преобразования JSON в XML выполняется в универсальной оболочке вызова службы, которая не знает базовой структуры данных и поэтому не может выполнять какие-либо запросы XPath на основе именованного узла. Дубликаты могут быть на любом уровне в XML.

Я дошел до стадии, когда у меня есть список имен повторяющихся узлов на каждом уровне, но я не уверен, что запрос Linq может использовать это для удаления всех узлов, кроме первого, с таким именем.

Мой код:

protected virtual void RemoveDuplicateChildren(XmlNode node)
{
    if (node.NodeType != XmlNodeType.Element || !node.HasChildNodes)
    {
        return;
    }

    var xNode = XElement.Load(node.CreateNavigator().ReadSubtree());
    var duplicateNames = new List<string>();

    foreach (XmlNode child in node.ChildNodes)
    {
        var isBottom = this.IsBottomElement(child); // Has no XmlNodeType.Element type children

        if (!isBottom)
        {
            this.RemoveDuplicateChildren(child);
        }
        else
        {
            var count = xNode.Elements(child.Name).Count();

            if (count > 1 && !duplicateNames.Contains(child.Name))
            {
                duplicateNames.Add(child.Name);
            }
        }
    }

    if (duplicateNames.Count > 0)
    {
        foreach (var duplicate in duplicateNames)
        {
            xNode.Elements(duplicate).SelectMany(d => d.Skip(1)).Remove();

        }
    }
}

Последняя строка кода явно неверна, но я не могу найти пример того, как ее переработать, чтобы получить, а затем удалить все, кроме первого совпадающего элемента.

ОБНОВЛЕНИЕ: я нашел два способа сделать это сейчас, один с использованием XElement и один с XmlNode, но на самом деле ни один из них не удаляет узлы.

Метод 1: -

foreach (var duplicate in duplicateNames)
{
    xNode.Elements(duplicate).Skip(1).Remove();
}

Способ 2: -

foreach (var duplicate in duplicateNames)
{
    var nodeList =  node.SelectNodes(duplicate);

    if (nodeList.Count > 1)
    {
        for (int i=1; i<nodeList.Count; i++)
        {
            node.RemoveChild(nodeList[i]);
         }
     }
}

Что мне не хватает?


person Valerie Metcalf    schedule 10.12.2014    source источник
comment
Если вы измените свой вопрос, никто из читателей этого не узнает. На этом этапе вам может потребоваться задать новый вопрос. Прокомментируйте один из ответов, если вы заимствовали из их материала, чтобы они знали, как уточнить свой ответ.   -  person Chuck Savage    schedule 10.12.2014
comment
Вам нужно сделать две вещи в своем коде: убедиться, что duplicateNames имеет все повторяющиеся имена, и две, что ваш xNode.Elements(duplicate) получает узлы, которые вы хотите удалить. В противном случае посмотрите мой ответ ниже, чтобы узнать, как изменить свой код.   -  person Chuck Savage    schedule 10.12.2014


Ответы (2)


Если вам не нужны повторяющиеся имена: (при условии отсутствия пространств имен)

XElement root = XElement.Load(file); // .Parse(string)
List<string> names = root.Descendants().Distinct(x => x.Name.LocalName).ToList();
names.ForEach(name => root.Descendants(name).Skip(1).Remove());
root.Save(file); // or root.ToString()
person Chuck Savage    schedule 10.12.2014
comment
Предположительно, этот root.Save (файл) - это то, что мне не хватает, но у меня нет файла, только строка XML, которую я загрузил в XmlDocument в начале обработки. XmlNode, который я передаю методу, является потомком этого документа. Возможно, это ключ, я должен обновить свой XML после работы с документом. - person Valerie Metcalf; 10.12.2014
comment
Да, вот и все, мой XmlDocument был обновлен, но я пропустил этап преобразования его обратно в строку. Я обновлю свой код удаления узла, чтобы он стал более эффективной версией. Спасибо за помощь. - person Valerie Metcalf; 10.12.2014

Вы можете попытаться решить проблему не на том уровне. В XML вполне допустимо иметь несколько узлов с одним и тем же именем. Структуры JSON с повторяющимися именами свойств должны быть недопустимыми. Вы должны попытаться выполнить эту очистку на уровне JSON, а не после того, как он уже был преобразован в XML.

Для очистки xml это может быть отправной точкой:

 foreach (XmlNode child 
   in node.ChildNodes.Distinct(custom comparer that looks on node names))
{
.....
}
person Adrian Zanescu    schedule 10.12.2014
comment
Я думал провести очистку на уровне JSON, но как мне это сделать? У меня нет контроля над созданием JSON, я использую только ответ от вызова службы. - person Valerie Metcalf; 10.12.2014
comment
Я не говорю о создании, но когда вы разбираете его с помощью JSONConvert. Просто пройдитесь по получившейся конструкции и удалите дубликаты. Это должен быть более чистый подход, чем делать то же самое на уровне xml. - person Adrian Zanescu; 12.12.2014
comment
AZ, какой класс мне следует использовать для просмотра структуры JSON перед преобразованием в XML? Мой метод выше хорошо работает с классом XmlNode, но я не могу найти эквивалент для JSON. - person Valerie Metcalf; 16.12.2014