更改嵌套节点的值

Posted

技术标签:

【中文标题】更改嵌套节点的值【英文标题】:Change Value of nested node 【发布时间】:2021-11-30 13:54:03 【问题描述】:

这似乎是一个简单的问题,但我似乎无法着手制定可行的解决方案。最终目标是更改下面突出显示的ConstantValue 元素的值。我的策略是找到Component 节点并从那里向下钻取。问题是不断返回一个空值,我不知道为什么。下面是我正在使用的代码,我正在使用的 xml。任何提示都会很棒。

   XDocument xmlDoc = XDocument.Parse(str);
        var items = xmlDoc.Descendants("Component")
                            .Where(x => x.Attribute("Name").Value == "axesInterface")
                            .FirstOrDefault();

<?xml version="1.0" encoding="utf-8"?>
<Document>
  <Engineering version="V17" />
  <DocumentInfo> 
  </DocumentInfo>
  <SW.Blocks.FB ID="0">
    <AttributeList>     
      <Interface><Sections></Sections></Interface>
      <MemoryLayout>Optimized</MemoryLayout>
      <MemoryReserve>100</MemoryReserve>
      <Name>EM00_CM01_Warp1</Name>
      <Number>31650</Number>
      <ProgrammingLanguage>LAD</ProgrammingLanguage>
      <SetENOAutomatically>false</SetENOAutomatically>
    </AttributeList>
    <ObjectList>    
      <SW.Blocks.CompileUnit ID="4" CompositionName="CompileUnits">
        <AttributeList>
          <NetworkSource>
            <FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
                <Parts>    
                  <Access Scope="GlobalVariable" UId="27">
                    <Symbol>
                      <Component Name="HMIAxisCtrl_Interface" />
                      <Component Name="axesInterface" AccessModifier="Array">
                        <Access Scope="LiteralConstant">
                          <Constant>
                            <ConstantType>DInt</ConstantType>
                            <ConstantValue>0</ConstantValue>
                          </Constant>
                        </Access>
                      </Component>
                    </Symbol>
                  </Access>   
                </Parts>
            </FlgNet>
          </NetworkSource>         
        </AttributeList>     
      </SW.Blocks.CompileUnit>       
    </ObjectList>
  </SW.Blocks.FB>
</Document>
  

【问题讨论】:

如果您包含格式良好的 XML 来说明您正在尝试做什么(作为文本,格式化为 代码我>)。这样,人们就可以获取您的 XML 并使用它。包括代码(XML 不是完全代码,但它足够接近)作为文本总是比作为图片更好 您需要像@Charlieface 在他的回答中那样处理 FlgNet 元素上的命名空间 【参考方案1】:

您可以使用 XQuery 获取正确的节点:

using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

//

var nm = new XmlNamespaceManager(new NameTable());
nm.AddNamespace("sm", "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4");

var node = xdoc.XPathSelectElement(@"//sm:Access[@Scope=""LiteralConstant""]/sm:Constant/sm:ConstantValue", nm);

node.Value = "Something Else";

dotnetfiddle

对于多个节点,将XPathSelectElement更改为XPathSelectElements

【讨论】:

谢谢。这是一个不错的选择。【参考方案2】:

在您的 XML 示例中,您可以使用这段代码在任意深度简单地获取指定节点:

XDocument xmlDoc = XDocument.Parse(str);
XElement node = xmlDoc.Descendants().FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
node.Value = "New value";
xmlDoc.Save(somewhere);

如果可能有多个节点名称为 "ConstantValue" - 那么只需将 FirstOrDefault 替换为 Where 并使用按名称过滤的 IEnumerable&lt;XElement&gt;

为什么x.Name == "ConstantValue"找不到它:

编辑:添加示例。

// That's your parent node
var componentNode = xmlDoc.Descendants()
                          .Where(x => x.Name.LocalName == "Component" 
                                   && (string)x.Attribute("Name") == "axesInterface");
// Get first ConstantValue node from parent Component node
var constantValueNode = componentNode.Descendants()
                                     .FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
// or get all ConstantValue nodes from parent Component node
var constantValueNodes = componentNode.Descendants()
                                     .Where(x => x.Name.LocalName == "ConstantValue");

if (constantValueNode is XElement)
    constantValueNode.Value = "New value"; 

您可以通过指定级别创建扩展方法来获取父节点:

public static class Extensions

    public static XElement GetParentNode(this XElement element, int parentLevel)
    
        XElement node = element.Parent;

        for (int i = 0; i < parentLevel; i++)
            node = node.Parent;

        return node;
    

在您的示例中,var parent = constantValueNode.GetParentNode(2); 将返回组件节点(具有属性 Name="axesInterface")。 var parent = constantValueNode.GetParentNode(12); 将返回根“文档”节点。

【讨论】:

很好的解决方案。问题是我给出的代码是一个更简单的版本,以保持简短。有很多ConstantValue 节点。是否可以指定一个父节点......甚至更好的是 parent 的父节点。例如,我可以看到属于特定组件的ConstantValue(在 xml 示例中向上 2 级)? @mrsargent,请检查我编辑了答案并在最后添加了一些代码。您可以通过Descendants()x.Name.LocalName = "YouNodeName" 获得any 深度的any 节点。或者我不明白你的要求:/

以上是关于更改嵌套节点的值的主要内容,如果未能解决你的问题,请参考以下文章

在链表中移动值之后。更改头部中的值会同时更改 2 个节点

从 xmlDocument 中选择特定的 xml 节点,然后更改 xml 节点的属性

如何读取和修改链接树中节点的值?

替换VB6中的XML节点中的值

是否可以使用 xmlreader 更改节点值?

jquery嵌套网页获取最外层父页面