【问题标题】:Loop to iterate up parent nodes until it finds specific tag循环遍历父节点,直到找到特定的标签
【发布时间】:2017-12-29 14:45:59
【问题描述】:

我正在做的是在 XML 文档中查找特定值,然后我想向上遍历每个父级,直到找到具有特定标记名称的父级。

List<XElement> fieldReferences = new List<XElement>();
fieldReferences.AddRange(element.XPathSelectElements(string.Format("descendant::nameValue[@idref='{0}']", fieldName)));
fieldReferences.AddRange(element.XPathSelectElements(string.Format("descendant::nameValue[@value='{0}']", fieldName)));

string parentIterator = ".Parent";
string attributeValue = ".Attribute('id').Value";
string parentElementName = ".Name";
foreach (var value in fieldReferences)
{
    var parentField = string.Format("{0}{1}", parentIterator, parentElementName);
    while (value + parentField != "private" || value + parentField != "public")
        {
            // keep appending .Parent until it finds what it needs
        }
    //var parentField = value.Parent.Parent.Parent.Parent.Attribute("id").Value;
    outputFields.Add(parentField, name.FirstOrDefault());
}

我遇到的问题是 parentField 将始终被评估为字符串,因此它永远不会真正检查 .Parent.Name 属性的值。

我不经常使用 C#,所以我确信有一种更简单的方法可以做到这一点,所以我的问题是:如何让我的 parentField 字符串评估我想要的方式,或者我如何在不同的方式来达到相同的最终结果?

编辑:这是我的 xml 的样子。 XPAthSelectElement 获取 nameValue 元素,我想遍历每个父元素,直到找到私有标记

<private id="I need to iterate upwards through each parent element until I find this one">
    <value>
      <request>
        <nameValues>
          <nameValue idref="I found this" />
          <nameValue value=""/>
        </nameValues>
      </request>
    </value>
  </private>

【问题讨论】:

  • 我已经阅读了您的问题几次,但在弄清楚您要做什么时仍然遇到一些问题。你能举一些XML的真实例子,以及你想匹配的东西的类型吗?
  • XElement 有一个父属性,它会为您提供另一个 XElement,那么为什么需要转换为字符串呢? msdn.microsoft.com/en-us/library/…。 value.Parent 为您提供父级,然后从那里获取有关父级的信息。
  • 是的,抱歉有点难以解释。
  • 这是我的 XML

标签: c# loops xelement


【解决方案1】:

因此,您实际上不需要执行这么多的字符串操作,然后就对 XPath 发疯了。找到子目标元素后,您可以迭代地使用XElement 上的Parent 属性,直到找到带有私有/公共标签的XElement。所以这给了我们这个代码:

foreach (XElement element in fieldReferences)
{
    XElement currentElement = element;
    while (currentElement.Parent != null)
    {
        currentElement = currentElement.Parent;
        if (currentElement.Name == "private" || currentElement.Name == "public") {
            outputFields.Add(currentElement, /* not sure what you want here */);
            break;
        }
    }
}

所以currentElement 将作为您示例中带有nameValue 标记的元素开始。在 while 循环中,每次迭代 currentElement 都会更改为其父节点,直到不再有父节点或 currentElement 已成为 privatepublic 标记。如果是后者,它会附加到您的结果中。

【讨论】:

  • 不能将它分配给元素,因为它是一个 foreach 迭代变量就是它所说的。
  • 我的错,它可以简单地分配给一个新变量。编辑了我的答案。
  • 是的,这就是我最终做的,我现在要试试,让你知道。
  • 这正是我所需要的。谢谢!
  • 我正在使用 XmlDocument 但仍然可以以相同的方式解决。我希望有一种更好的方法,而不必编写自己的方法。我应该只是使用智能感知来实现我必须编写一个方法。
【解决方案2】:

您可以使用XElement.Ancestors 函数获取包含您找到的节点的所有元素的列表,然后只需使用 LINQ 选择您想要的元素。不需要任何循环。

var results = fieldReferences.Select(element => element.Ancestors()
                                                       .Where(ancestor => ancestor.Name == "public" ||
                                                                          ancestor.Name == "private")
                                                       .FirstOrDefault());

请注意,这将一直沿树向上,如果有多个匹配的祖先(或没有匹配的祖先),则可能会出现问题。让我知道这对您来说是否有问题,以及在这种情况下您想要什么结果,我可以进行调整。

【讨论】:

  • 这种方法的唯一问题是我的 fieldReferences 列表中可能有很多项目
  • 它应该为所有这些找到正确的元素。这不是你想要的吗?如果 fieldReferences包含 3 个 XElement 实例,results 将包含 3 个 XElement 实例,它们是名为“公共”或“私有”元素的祖先。
  • 现在我有了一些工作,我会将这种方法添加到我的重构机会列表中,谢谢!
猜你喜欢
  • 2021-10-02
  • 1970-01-01
  • 2014-06-28
  • 2021-07-16
  • 2014-09-28
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
  • 2018-09-02
相关资源
最近更新 更多