none
XML to LinQ modify attribute value; best way RRS feed

  • Question

  • Hello,
    I need to read an XML file that has these 2 sides. It can happen that a side does not have the element "PROCESSSTEP".
    If the element exists, I have to change the attribute from "HANDLING" it and save it.
    <PRODUCT>
    <NAME value="RECEIVER" />
    <SIDE1>
    	<POSITIONS>
    		<POSITION>
    			<NUMBER value="1" />
    			<ACTIVE value="1" />
    			<PROCESSSTEP>
    				<STEP value="MarkRed">
    					<HANDLING value="4" />
    				</STEP>
    				<STEP value="MarkGreen">
    					<HANDLING value="0" />
    					<PRINTGROWTHMINVALUE value="F" />
    				</STEP>
    			</PROCESSSTEP>
    		</POSITION>
    </SIDE1>
    <SIDE2>
    	<POSITIONS>
    		<POSITION>
    			<NUMBER value="1" />
    			<ACTIVE value="1" />
    		</POSITION>
    </SIDE2>
    </PRODUCT>
    How can I do this?
    It can be missing for one side, for both sides or just available, all possibilities.
    My attempt to make it work for side 1, side 2 doesn't because the element is missing, although it's written like this.
    ?.Element("POSITION")?.Element("PROCESSSTEP")?.Elements()
    Does anyone have a solution, any suggestions on how to solve it?
    Thanks in advance.

    With best regards Markus
    XDocument productXML = XDocument.Load(productFilePath);
    int entries = 1;
    bool changed = false;
    
    for (int side = 1; side <= 2; side++)
    {
    	foreach (XElement xePosition in programXML.Element($"SIDE{side}").Element("POSITIONS")?.Element("POSITION")?.Element("PROCESSSTEP")?.Elements())
    	{
        	if (xePosition.Attribute("value")?.Value == "MarkGreen")
    		{
    			foreach (XElement xeStep in xePosition?.Elements())  
    			{
    				switch (xeStep?.Name.LocalName)
    				{
    					case "HANDLING":
    						if (newParameterset1.Length > 0)
    						{
    							xeStep.SetAttributeValue("value", "NewHandlingValue");
    							changed = true;
                                               


    Thursday, October 24, 2019 4:47 PM

Answers

  • Are you using XML you have included? It does not have closing </POSITIONS> element.

    I know.

    Ist only an extract. XML ist right.

    How so I get the values into a own structure?

    Into objects to work with objects?

    Best regards Markus

    you could use Linq to XML loading the XML into a XMLDocument and then projecting, a custom projection,  into a concrete object that would create a collection of custom objects that are read/write objects.

    https://www.dotnetcurry.com/linq/564/linq-to-xml-tutorials-examples

    https://csharp-station.com/Tutorial/Linq/Lesson02

    • Marked as answer by Markus Freitag Friday, November 1, 2019 10:24 AM
    Saturday, October 26, 2019 3:31 PM
  • I have not used Linq-2-XML in sometime, but you could just use one of the first two examples in the tutorial and look at the content of the collection using Debug Quickwatch and see how it has the data loaded. And from there, you can use some of the other examples on how to address the data with using Linq.
    • Marked as answer by Markus Freitag Friday, November 1, 2019 10:24 AM
    Monday, October 28, 2019 7:44 AM

All replies

  • Are you using XML you have included? It does not have closing </POSITIONS> element.
    Friday, October 25, 2019 4:44 AM
  • One of possibilities:

    using System.Xml.XPath;
    
    . . .
    
    string xml = @"
    <PRODUCT>
    	<NAME value='RECEIVER' />
    	<SIDE1>
    		<POSITIONS>
    			<POSITION>
    				<NUMBER value='1' />
    				<ACTIVE value='1' />
    				<PROCESSSTEP>
    					<STEP value='MarkRed'>
    						<HANDLING value='4' />
    					</STEP>
    					<STEP value='MarkGreen'>
    						<HANDLING />
    						<PRINTGROWTHMINVALUE value='F' />
    					</STEP>
    				</PROCESSSTEP>
    			</POSITION>
    		</POSITIONS>
    	</SIDE1>
    	<SIDE2>
    		<POSITIONS>
    			<POSITION>
    				<NUMBER value='1' />
    				<ACTIVE value='1' />
    			</POSITION>
    		</POSITIONS>
    	</SIDE2>
    </PRODUCT>
    ";
    
    XDocument xdoc = XDocument.Parse( xml );
    var handling_elements = xdoc.XPathSelectElements( "(/PRODUCT/SIDE1 | /PRODUCT/SIDE2)/POSITIONS/POSITION/PROCESSSTEP/STEP/HANDLING" );
    
    foreach( var e in handling_elements ) e.SetAttributeValue( "value", "new value" );
    
    Console.WriteLine( xdoc );


    • Edited by Viorel_MVP Friday, October 25, 2019 5:06 AM
    Friday, October 25, 2019 5:02 AM
  • Are you using XML you have included? It does not have closing </POSITIONS> element.

    I know.

    Ist only an extract. XML ist right.

    How so I get the values into a own structure?

    Into objects to work with objects?

    Best regards Markus

    Friday, October 25, 2019 5:28 AM
  • But what's happen, if side 2 not exist?

    Maybe I need a class for STEP.

    Greetings Markus

    Friday, October 25, 2019 5:30 AM
  • Hi Markus Freitag, 

    Thank you for posting here.

    I make a change to the Viorel_’s code, and hope it can help you.

            static void Main(string[] args)
            {
                string path = @"path";
                Regex regex = new Regex(@"SIDE");
                XDocument xdoc = XDocument.Load(path);
                var eles = xdoc.Descendants();
    
                string str = "(";
                foreach (var ele in eles)
                {
                    if (regex.IsMatch(ele.Name.ToString()))
                    {
                        str += "/PRODUCT/" + ele.Name.ToString() + " | ";
                    }
                }
                str = str.Substring(0,str.Length-3) + ")";
                
                var handling_elements = xdoc.XPathSelectElements(str + "/POSITIONS/POSITION/PROCESSSTEP/STEP/HANDLING");
                foreach (var e in handling_elements) e.SetAttributeValue("value", "new value");
                Console.WriteLine(xdoc);
                Console.ReadLine();
            }

    Result without SIDE2 node:

    Best Regards,

    Xingyu Zhao



    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, October 25, 2019 9:19 AM
    Moderator
  • Hello Xingyu Zhao,

    First, thanks for your reply.
    Target is to change the attribute values PARAMETER_A   & PARAMETER_B  by code.
    I'm looking for a good way and without RegEx.

    <SIDE1>
      <POSITIONS>
    	<POSITION>
    	  <NUMBER value="1" />
    	  </PROCESS>
    	</POSITION>
    	<POSITION>
    	  <NUMBER value="2" />
    	  <PROCESS>
    		<STEP value="MarkPosition">
    		  <PARAMETER_A value="T_NEW1" />
    		  <PARAMETER_B value="T_NEW2" />
    		</STEP>

    for (int side = 1; side <= 2; side++)
    {
    	//                      var result = xDocProgramID.Element("ROOT").Element("PROGRAM").Element($"SIDE{side}").Element("POSITIONS")?.Element("POSITION")?.Elements().Where(x => x.HasElements && x.Name.LocalName == "PROCESS").ToList();
    	var resultPositions = xDocProgramID.Element("ROOT").Element("PROGRAM").Element($"SIDE{side}").Element("POSITIONS")?.Elements();
    	
        var resultPositions = xDocProgramID.Element("ROOT").Element("PROGRAM").Element($"SIDE{side}").Element("POSITIONS")?.Elements();
    
    	var resultProcess = resultPositions.Elements().Where(x => x.Name.LocalName == "PROCESS" && ((XElement)x.FirstNode).Name == "STEP" && ((XElement)x.FirstNode).Attribute("value") == "STEP");
    					
    	
    Can I simply select it, navigate and call a replace?
    PARAMETER_A    Change the value attribute.

    I can go this way. I'm looking for a better way!
    If I find a better way, I will use it, another I go step by step.


    This works well, but I think there is a better way, not go all in the loop.

    for (int side = 1; side <= 2; side++)
    {
    	foreach (XElement xePositions in programXML.Element("ROOT").Element("PROGRAM").Element($"SIDE{side}").Element("POSITIONS")?.Elements())
    	{
    		foreach (XElement xePosition in xePositions.Elements())
    		{
    			if (xePosition.Name.LocalName != "PROCESS")
    				continue;
    			foreach (XElement xeProcess in xePosition.Elements())
    			{
    				if (xeProcess.Attribute("value")?.Value == "MarkPosition")
    				{
    					foreach (XElement xeStep in xeProcess?.Elements()) 
    					{

    Best Regards Markus



    Saturday, October 26, 2019 10:27 AM
  • Are you using XML you have included? It does not have closing </POSITIONS> element.

    I know.

    Ist only an extract. XML ist right.

    How so I get the values into a own structure?

    Into objects to work with objects?

    Best regards Markus

    you could use Linq to XML loading the XML into a XMLDocument and then projecting, a custom projection,  into a concrete object that would create a collection of custom objects that are read/write objects.

    https://www.dotnetcurry.com/linq/564/linq-to-xml-tutorials-examples

    https://csharp-station.com/Tutorial/Linq/Lesson02

    • Marked as answer by Markus Freitag Friday, November 1, 2019 10:24 AM
    Saturday, October 26, 2019 3:31 PM

  • https://www.dotnetcurry.com/linq/564/linq-to-xml-tutorials-examples

    https://csharp-station.com/Tutorial/Linq/Lesson02

    Dear DA924x,

    <SIDE1>
      <POSITIONS>
    	<POSITION>
    	  <NUMBER value="1" />
    	  </PROCESS>
    	</POSITION>
    	<POSITION>
    	  <NUMBER value="2" />
    	  <PROCESS>
    		<STEP value="MarkPosition">
    		  <PARAMETER_A value="T_NEW1" />   #########
    		  <PARAMETER_B value="T_NEW2" />
    		</STEP>

    Ok, thanks.

    Can you please show me how you would select the element from the ROOT and analyze the elements with attribute = MarkPosition ?

    Thanks and have a nice Sunday.

    Best Regards Markus

    Sunday, October 27, 2019 1:48 PM
  • I have not used Linq-2-XML in sometime, but you could just use one of the first two examples in the tutorial and look at the content of the collection using Debug Quickwatch and see how it has the data loaded. And from there, you can use some of the other examples on how to address the data with using Linq.
    • Marked as answer by Markus Freitag Friday, November 1, 2019 10:24 AM
    Monday, October 28, 2019 7:44 AM