views:

926

answers:

3

As in, if I have an XML Document

<root a="value">
    <item name="first">
        x
        <foo name = "firstgrandchild">There is nothing here</foo>
        y
        <foo name = "secondgrandchild">There is something here</foo> 
    </item>
    <item name="second">
        xy
        <foo/>
        ab
    </item>
</root>

I want to first find the first occurrence of node "item" and then update the attribute, and then I want to update the first occurrence of node "foo" and then update the attribute etc.,

My Code is as below

myDoc.Load("Items2.xml");
myNode = myDoc.DocumentElement;
mySearchNode = myNode.SelectSingleNode("/root/item");
mySearchNode.Attributes["name"].Value = "Joel";
Console.WriteLine(mySearchNode.OuterXml);
mySearchChildNode = mySearchNode.SelectSingleNode("/item/foo");
Console.WriteLine(mySearchChildNode.OuterXml);

While, the first search and update of attribute works fine, the second one fails as mySearchNode.SelectSingleNode returns null.

Question - Is there something fundamentally that is wrong with this code? Why does not the SelectSingleNode work as expected in the second instance, as far as it is concerned, I am executing it on a XmlNode of type Element.

Kindly assist.

Many Thanks,

+2  A: 

mySearchNode is the item node, so if foo is a child of item your second xpath should simply be "foo"

AakashM
+2  A: 

Your second XPath query should be without the leading slash. / means "root of document". If you omit the slash, the query will be relative to your mySearchNode variable. You also should not include "item" again, your query is relative to the "item" node you selected. In code:

myDoc.Load("Items2.xml");
myNode = myDoc.DocumentElement;
mySearchNode = myNode.SelectSingleNode("/root/item");
mySearchNode.Attributes["name"].Value = "Joel";
Console.WriteLine(mySearchNode.OuterXml);
mySearchChildNode = mySearchNode.SelectSingleNode("foo");
Console.WriteLine(mySearchChildNode.OuterXml);
driis
A: 

You can do a SelectNodes and then loop through all the item nodes. For each item you should further do a SelectNodes on foo nodes. You should check for the node count and also whether the attribute name exists or not for both item and foo nodes. You can use this code:

/// <summary>
/// Changes the xml in sourceFileName and writes the changed xml to destFileName
/// </summary>
public static void ProcessNames(string sourceFileName, string destFileName)
{
    XmlDocument xmlDoc = new XmlDocument();
    XmlTextWriter xtw = null;
    xmlDoc.Load(sourceFileName);

    try
    {
        // Parse through all the item nodes and process them
        XmlNodeList itemList = xmlDoc.SelectNodes("//root/item");

        if (itemList.Count > 0)
        {
            foreach (XmlNode item in itemList)
            {
                // Change the name attribute, if it exists
                if (item.Attributes["name"] != null)
                {
                    string newItemName = item.Attributes["name"].Value + "Joel";
                    item.Attributes["name"].Value = newItemName;
                }

                // Parse through all the foo nodes and process them
                XmlNodeList fooList = item.SelectNodes("foo");

                if (fooList.Count > 0)
                {
                    foreach (XmlNode foo in fooList)
                    { 
                        // Change the name attribute, if it exists
                        if (foo.Attributes["name"] != null)
                        {
                            string newFooName = foo.Attributes["name"].Value + "Joel";
                            foo.Attributes["name"].Value = newFooName;
                        }
                    }
                }

            }

            xtw = new XmlTextWriter(destFileName, Encoding.UTF8);
            xmlDoc.WriteContentTo(xtw);
        }

    }
    finally
    {
        xtw.Close();
    }
}
Rashmi Pandit