mrsargent
mrsargent

Reputation: 2442

Change Value of nested node

This seems like a simple question but I can't seem to get started on a working solution. The final goal is to change the value of the ConstantValue element highlighted below. My strategy is to find the Component node and drill down from there. The problem is that keep returning a null and I'm not sure why. Below is the code I'm using a the xml I'm using. Any hints would be great.

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

enter image description here

<?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>
  

Upvotes: 0

Views: 316

Answers (2)

Auditive
Auditive

Reputation: 2049

On your XML example, you can simply get specified node with this piece of code at any depth:

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

If there may be multiple nodes with "ConstantValue" name - then just replace FirstOrDefault with Where and work with filtered by names IEnumerable<XElement>.

Why it isn't find by x.Name == "ConstantValue":

enter image description here

EDIT: added samples.

// 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"; 

You can create extension method to get parent node by specifying level:

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

In your example, var parent = constantValueNode.GetParentNode(2); will return Component node (with attribute Name="axesInterface"). var parent = constantValueNode.GetParentNode(12); will return root "Document" node.

Upvotes: 1

Charlieface
Charlieface

Reputation: 72289

You can use XQuery to get the correct node:

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

For multiple nodes, change XPathSelectElement to XPathSelectElements

Upvotes: 2

Related Questions