【问题标题】:How can I remove duplicate, invalid, child nodes from an XML document using Linq to XML?如何使用 Linq to XML 从 XML 文档中删除重复的、无效的子节点?
【发布时间】:2014-12-10 16:32:32
【问题描述】:

我正在使用 JsonConvert 从从 HttpWebRequest 调用中检索到的 JSON 创建 XML。我返回的 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,但实际上都没有删除节点。

方法一:-

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

方法二:-

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]);
         }
     }
}

我错过了什么?

【问题讨论】:

  • 如果你修改了你的问题,没有看过帖子的人会知道。那时您可能需要做一个新问题。如果您从他们的材料中借用答案,请评论其中一个答案,以便他们知道如何澄清他们的答案。
  • 你需要在你的代码中做两件事,验证duplicateNames 有所有的重复名称,以及你的xNode.Elements(duplicate) 正在获取你想要删除的节点。否则请看我下面的答案,看看如何修改你的代码。

标签: c# xml linq xelement


【解决方案1】:

如果您不想要任何重复的名称:(假设没有命名空间)

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()

【讨论】:

  • 大概是“root.Save(file)”是我所缺少的,但我没有文件,只是我在开始时加载到 XmlDocument 中的 XML 字符串处理。我传递给该方法的 XmlNode 是该文档的子文档。也许这就是关键,我应该在处理完文档后更新我的 XML。
  • 是的,就是这样,我的 XmlDocument 已更新,但我错过了将其转换回字符串的步骤。我会将我的节点删除代码更新给我们您更高效的版本。感谢您的帮助。
【解决方案2】:

您可能会尝试在错误的级别上解决问题。在 XML 中,拥有多个同名节点是完全有效的。具有重复属性名称的 JSON 结构应该是无效的。您应该尝试在 JSON 级别而不是在它已经转换为 XML 之后进行此清理。

对于 xml 清理,这可能是一个起点:

 foreach (XmlNode child 
   in node.ChildNodes.Distinct(custom comparer that looks on node names))
{
.....
}

【讨论】:

  • 我曾想过在 JSON 级别进行清理,但我该怎么做呢?我无法控制 JSON 的创建,我只使用来自服务调用的响应。
  • 我不是在说创建,而是当您使用 JSONConvert 解析它时。只需遍历生成的结构并删除重复项。这应该是比在 xml 级别做同样的事情更干净的方法
  • AZ,在转换为 XML 之前,我应该使用哪个类来遍历 JSON 结构?我上面的方法适用于 XmlNode 类,但我找不到 JSON 的等价物。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 2020-03-27
  • 1970-01-01
  • 2011-08-28
相关资源
最近更新 更多