C# SelectSingleNode - 可以递归使用吗?

发布于 2024-07-24 12:43:37 字数 1036 浏览 6 评论 0原文

例如,如果我有一个 XML 文档,

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

我想首先找到节点“item”的第一次出现,然后更新属性,然后我想更新节点“foo”的第一次出现,然后更新属性等,

我的代码如下

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

虽然,属性的第一次搜索和更新工作正常,但第二次失败,因为 mySearchNode.SelectSingleNode 返回 null。

问题 - 这段代码是否有根本性的错误? 为什么 SelectSingleNode 在第二个实例中没有按预期工作,就其而言,我在 Element 类型的 XmlNode 上执行它。

请协助。

非常感谢,

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,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

像你 2024-07-31 12:43:37

您的第二个 XPath 查询应该不带前导斜杠。 / 表示“文档的根”。 如果省略斜杠,查询将相对于您的 mySearchNode 变量。 您也不应该再次包含“item”,您的查询与您选择的“item”节点相关。 在代码中:

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

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);
上课铃就是安魂曲 2024-07-31 12:43:37

mySearchNode 是 item 节点,因此如果 fooitem 的子节点,您的第二个 xpath 应该只是 "foo"

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

凤舞天涯 2024-07-31 12:43:37

您可以执行 SelectNodes,然后循环遍历所有项目节点。 对于每个项目,您应该进一步在 foo 节点上执行 SelectNodes。 您应该检查节点计数以及 item 和 foo 节点的属性名称是否存在。
您可以使用此代码:

/// <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();
    }
}

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();
    }
}
甜是你 2024-07-31 12:43:37

根据我的理解,主要问题是,在节点上调用的 SelectNode(此处:mySearchNode.SelectSingleNode)无法按预期工作!!!
它不会从给定节点开始搜索,而是始终从根文档开始搜索。 (另请参阅源代码,其中对于节点,相关的 XmlDocument 节点用于启动 XPathNavigator)
我根本没想到会有这种行为!
最好使用应该按预期工作的 XDocument。

From my understanding the main problem is, that SelectNode(s) called on a node (here: mySearchNode.SelectSingleNode) does not work as expected!!!
It does not search beginning with the given node, but always from root document. (see also the source code, where for a node the related XmlDocument node is used to initiate the XPathNavigator)
I did not expect this behaviour at all!
Better use XDocument that should work as expected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文