我需要在父级而不是子级中使用InsertBefore或InsertAfter

时间:2013-11-14 20:22:32

标签: c# xml nodes xmldocument xmlnodelist

这是我的Xml

<root>
<categories>
    <recipe id="RecipeID2">
        <name>something 1</name>
    </recipe>
    <recipe id="RecipeID2">
        <name>something 2</name>
    </recipe>
    <recipe id="RecipeID3">
        <name>something 3</name>
    </recipe>
</categories>
</root>

我正在解析客户想要在

之前或之后插入新配方的所有食谱
XmlDocument xmlDocument = new XmlDocument();

xmlDocument.Load("thexmlfiles.xml");

XmlNodeList nodes = xmlDocument.SelectNodes("/root/categories//Recipe");

foreach (XmlNode node in nodes)
{
    if (node.Attributes["id"].InnerText == comboBoxInsertRecipe.Text)
    {
        node.InsertAfter(xfrag, node.ChildNodes[0]);
    }
}

预期产出:

<root>
<categories>
    <recipe id="RecipeID2">
        <name>something 1</name>
    </recipe>
    <recipe id="RecipeID2">
        <name>something 2</name>
    </recipe>
    <recipe id="NewRecipe4">
        <name>new Recipe 4</name>
    </recipe>
    <recipe id="RecipeID3">
        <name>something 3</name>
    </recipe>
</categories>
</root>

但是当我插入新的食谱时,它就像这样

<root>
<categories>
    <recipe id="RecipeID2">
        <name>something 1</name>
    </recipe>
    <recipe id="RecipeID2">
        <name>something 2</name>
    </recipe>
    <recipe id="RecipeID3">
        <name>something 3</name>
        <recipe id="NewRecipe4">
            <name>new Recipe 4</name>
        </recipe>
    </recipe>
</categories>
</root>

新食谱位于另一个食谱内,但不属于类别

2 个答案:

答案 0 :(得分:2)

首先,我建议使用LINQ-to-Xml。本答案中的L2Xml样本。

XDocument xmlDocument = XDocument.Load("thexmlfiles.xml");
var root = xmlDocument.Root;
var recipes = root.Element("categories").Elements("recipe");

其次,获取您希望在之前/之后插入的节点的句柄/引用。

var currentRecipe = recipes.Where(r => r.Attribute("id") == "RecipeID3")
   .FirstOrDefault();

...然后根据需要添加(使用XElement.AddAfterSelfXElement.AddBeforeSelf):

void AddNewRecipe(XElement NewRecipe, bool IsAfter, XElement CurrentRecipe) {
   if(IsAfter) {
      CurrentRecipe.AddAfterSelf(NewRecipe);
   } else {
      CurrentRecipe.AddBeforeSelf(NewRecipe);
   }
}

答案 1 :(得分:1)

您正在错误的文档级别添加新节点。必须将元素(如指向)添加到类别节点,而不是兄弟节点。您有两个选项可以找到正确的节点,然后将节点添加到正确的位置:

  • 当你这样做时,遍历寻找匹配的所有节点
  • 直接使用XPath找到节点 / path / element [@ attribute ='attributeName']

将节点添加到正确位置的示例如下所示:

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(@"d:\temp\thexmlfile.xml");
XmlNodeList nodes = xmlDocument.SelectNodes("/root/categories/recipe");
// root node
XmlNodeList category = xmlDocument.SelectNodes("/root/categories");
// test node for the example
var newRecipe = xmlDocument.CreateNode(XmlNodeType.Element, "recipe", "");
var newInnerNode = xmlDocument.CreateNode(XmlNodeType.Element, "name", "");
newInnerNode.InnerText = "test";
var attribute = xmlDocument.CreateAttribute("id");
attribute.Value = "RecipeID4";
newRecipe.Attributes.Append(attribute);
newRecipe.AppendChild(newInnerNode);
// variant 1; find node while iteration over all nodes
foreach (XmlNode node in nodes)
{
    if (node.Attributes["id"].InnerText == "RecipeID3")
    {
        // insert into the root node after the found node
        category[0].InsertAfter(newRecipe, node);
    }
}
// variant 2; use XPath to select the element with the attribute directly
//category[0].InsertAfter(newRecipe, xmlDocument.SelectSingleNode("/root/categories/recipe[@id='RecipeID3']"));
// 
xmlDocument.Save(@"d:\temp\thexmlfileresult.xml");

输出结果为:

<root>
    <categories>
        <recipe id="RecipeID1">
            <name>something 1</name>
        </recipe>
        <recipe id="RecipeID2">
            <name>something 2</name>
        </recipe>
        <recipe id="RecipeID3">
            <name>something 3</name>
        </recipe>
        <recipe id="RecipeID4">
            <name>test</name>
        </recipe>
    </categories>
</root>

同样建议您可以使用LINQ2XML进行此操作。代码可能如下所示:

// load document ...
var xml = XDocument.Load(@"d:\temp\thexmlfile.xml");
// find node and add new one after it
xml.Root                        // from root
    .Elements("categories")     // find categories
    .Elements("recipe")         // all recipe nodes
    .FirstOrDefault(r => r.Attribute("id").Value == "RecipeID3")    // find node by attribute
    .AddAfterSelf(new XElement("recipe",                            // create new recipe node
                    new XAttribute("id", "RecipeID4"),              // with attribute
                    new XElement("name", "test")));                 // and content - name node
// and save document ...
xml.Save(@"d:\temp\thexmlfileresult.xml");

输出相同。 LINQ2XML在许多方面比XmlDocument更容易使用。例如,子节点的选择可以更容易,并且您不需要XPath字符串:

xml.Descendants("recipe");

LINQ2XML值得尝试一下。