【问题标题】:C# XDocument remove attributes not in a listC# XDocument 删除不在列表中的属性
【发布时间】:2017-08-29 14:02:23
【问题描述】:

我在 C# 中查询一个 Web 服务,它返回 XML,我已将其解析为 XDocument。我删除了一个无关的节点,该节点返回有关服务调用的统计信息,并留下如下内容:

<xml>
   <element attr1="1" attr2="2" attr3="3" attr4="4" ... attrN="N" />
   <element attr1="a" attr2="b" attr3="c" ... attrN="N" />
   ...
   <element attr1="!" attr2="?" attr3=":" attr4="" ... attrN="N /> 
</xml>

我真的只对检索 attr2 和 attr3 感兴趣,所以我想删除所有其他属性以结束:

<xml>
   <element attr2="2" attr3="3" />
   <element attr2="b" attr3="c" />
   ...
   <element attr2="?" attr3=":" />
</xml>

但是,这是我正在使用的相关代码,它只是删除 attr1:

String[] WS_LookupFields = "attr2,attr3".Split(',');
var output = XDocument.Parse(WS_Output_Raw);
output.Descendants("system").Remove(); //removes query statistics
foreach (XAttribute attribute in output.Descendants("element").Attributes())
{
    if (!Array.Exists<String>(WS_LookupFields, fieldname => attribute.Name == fieldname)) attribute.Remove();
}

问题是 foreach only 返回 attr1 而不是 attr1 通过 attrN。有什么建议吗?

【问题讨论】:

  • String[] WS_LookupFields = "attr1,attr2".Split(','); 替换为String[] WS_LookupFields = "attr2,attr3".Split(',');。如果能解决您的问题,请告诉我
  • 抱歉,打错了;不适用于实际代码。

标签: c# xml linq linq-to-xml


【解决方案1】:

使用这个:

var WS_LookupFields = "attr2,attr3".Split(',').ToList();
var output = XDocument.Parse(WS_Output_Raw);
foreach (var loElement in output.Descendants("element"))
{
    //loElement.Attributes()
    //    .Where(item => !WS_LookupFields.Any(name => item.Name == name))
    //    .ToList()
    //    .ForEach(item => item.Remove());
    //UPDATE: Charles Mager
    loElement.Attributes()
        .Where(item => !WS_LookupFields.Any(name => item.Name == name))
        .Remove();
}

或者:

foreach (var loAttribute in output.Descendants("element").Attributes().ToList())
{
    if (!WS_LookupFields.Contains(loAttribute.Name.ToString()))
        loAttribute.Remove();
}

【讨论】:

  • 完美。我错过了对 System.Linq 的引用,但是一旦我添加了它,您的代码就运行得非常好。 (我想知道为什么我没有在方法列表中看到诸如 Where 之类的东西。)
  • 我强烈建议不要使用.ToList().ForEach(..)(请参阅this question)。令人高兴的是,您可以只替换为 .Remove()
  • @Charles Mager:我不知道 foreach 的问题。基本上,使用它应该没有问题。但你是对的,你可以直接从IEnumerable&lt;XAttribute&gt; 拨打Remove。谢谢
【解决方案2】:

有些方法旨在从树中删除 xml 内容。您已经将一个用于Elements,也将其用于属性。

构建查询以选择要删除的属性,然后将其删除。

var attrs = new HashSet<string>("attr2,attr3".Split(','));
foreach (var e in output.Descendants("element"))
    e.Attributes().Where(a => !attrs.Contains(a.Name.LocalName)).Remove();

// or simply
output.Descendants("element")
    .Attributes()
    .Where(a => !attrs.Contains(a.Name.LocalName))
    .Remove();

【讨论】:

    【解决方案3】:

    我认为你需要两个循环:

    // Loop all descendants
    foreach (var element in output.Descendants("element"))
    {
        // for that element, loop all attributes
        foreach (var attribute in element.Attributes())
        {
        if (!Array.Exists<String>(WS_LookupFields, fieldname => attribute.Name == 
    fieldname)) attribute.Remove();
        }
    }
    

    【讨论】:

    • 请注意,这是完全未经测试的
    • 我试过了,忘了说。它对输出没有任何影响。
    【解决方案4】:

    只需将 ToArray() 方法添加到您的 foreach 循环中:

    foreach (XAttribute attribute in output.Descendants("element").Attributes().ToArray())
    {
        if (!Array.Exists<String>(WS_LookupFields, fieldname => attribute.Name == fieldname)) attribute.Remove();
    }
    

    【讨论】:

      【解决方案5】:

      只需创建具有所需属性的新文档

      var required = new HashSet<string> { "attr2", "attr3" };
      
      var elements = original.Descendants("element").Select(element =>
      {
          var attributes = element.Attributes().Where(a => required.Contains(a.Name.LocalName));
          return new XElement(element.Name, attributes);
      });
      
      var root = new XElement(original.Root.Name, elements);
      var newDocument = new XDocument(original.Declaration, root);
      

      【讨论】:

        猜你喜欢
        • 2013-08-15
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-26
        • 1970-01-01
        相关资源
        最近更新 更多