Massey
Massey

Reputation: 1127

How to update XML attributes using Linq to XML in C#

I have a collection of custom objectswhich has storeid. I also have an XmlDocument which I query data from the database and constructed in memory. The StoreId property in the custome object corresponds to the "Value" object in the XML.I have to loop through my custom collection and match the StoreId to the "Value" attribute in the XML and set its "Checked" attribute equal to true. As shown in the XML below, all the "Checked" attribute values are set to false at the start.

<Tree>
<Node Text="Whole ">
  <Node Text="America">
     <Node Text="NewYork">
        <Node Value="28" Checked="false " Text="NY1" />
        <Node Value="29" Checked="false " Text="NY2" />
     </Node>
     <Node Text="Houston">
        <Node Value="13 " Checked="false " Text="H1" />
        <Node Value="14 " Checked="false " Text="H2" />
        <Node Value="16 " Checked="false " Text="H3" />
        <Node Value="19 " Checked="false " Text="H4" />
        <Node Value="26 " Checked="false " Text="H5" />
     </Node>
     <Node Text="GeorgeTown">
        <Node Value="21 " Checked="false " Text="G1" />
        <Node Value="23 " Checked="false " Text="G2" />
        <Node Value="25 " Checked="false " Text="G3" />
     </Node>
  </Node>
  </Node>
  </Tree>

My code is:

public class MyObject
 {
    public string StoreId { get; set; }
    public bool HasValue { get; set; }
 }

 Code to update XML:
 XmlDocument baseDocument = ConstructXMLFromDataBase();
 XmlNodeList dataNodes = baseDocument.SelectNodes("//Tree/Node/Node/Node");

 List<MyObject> myCollections = GetMyCollection();

 foreach (var myCollection in myCollections)
 {
    foreach (XmlNode node in dataNodes)
    {
      //code to update
    }               
 }

I believe it can be done easily with Linq to XML and I am very new to Linq to XML. Moreover most of the samples available on the internet are about updating XML loaded from a disc and not constructed in memory.

Thanks

Upvotes: 0

Views: 4221

Answers (3)

Fabio
Fabio

Reputation: 32445

If you want using LINQ to Xml you need change ConstructXMLFromDataBase method to return XDocument object.

XDocument document = ConstructXMLFromDataBase();

If you constructed xml from the string loaded from database you can use

return XDocument.Parse(validXmlString);

In case your xml is not valid document (without <?xml.. tag) then you can return XElement

return XElement.Parse(yourXmlString);

Or use "dirty" workaround and convert it to XDocument

XmlDocument xmlDocument = ConstructXMLFromDataBase();
XDocument document;
using (var nodeReader = new XmlNodeReader(xmlDocument))
{
    document = XDocument.Load(nodeReader);    
}

And then update existed nodes. This code will work in both cases, where document is XDocument or document is XElement

var storeIdCollection = GetMyCollection().Select(myObject => myObject.StoreId);
var storeIds = new HashSet<string>(storeIdCollection);

var nodes = document.Descendants.Where(node => node.Attribute("Value") != null);
foreach(var node In nodes)
{
    var storeId = node.Attribute("Value").Value;
    If (storeIds.Contains(storeId))
    {
        node.Attribute("Checked").Value = "true";
    }
}

Upvotes: 0

L.B
L.B

Reputation: 116098

I find Linq2Xml + XPath easier to use

var id = "19";
var xdoc = XDocument.Load(filename);

xdoc.XPathSelectElement($"//Node[@Value='{id}']").Attribute("Checked").Value = "true";

xdoc.Save(filename);

Upvotes: 0

CoolBots
CoolBots

Reputation: 4869

You can accomplish the update with your method by adding one more inner loop:

foreach (XmlNode checkedNode in node.ChildNodes)
{
    checkedNode.Attributes["Checked"].Value = "true";
}

If you want to use Linq, you should switch to XDocument:

var doc = XDocument.Parse(xml);     //xml is a string, can be returned from a function, 
                                    //built dynamically, etc.

var nodesToUpdate = doc.Descendants("Node")
                       .Where(n => n.Attributes("Checked").FirstOrDefault() != null);

foreach (var node in nodesToUpdate)
{
    //TODO: check update conditions, etc.

    node.Attribute("Checked").Value = "true";
}

Upvotes: 4

Related Questions