这是我的 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>
新配方位于另一个配方中,但不在类别内
首先,我建议使用 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.AddAfterSelf
或 XElement.AddBeforeSelf
):
void AddNewRecipe(XElement NewRecipe, bool IsAfter, XElement CurrentRecipe) {
if(IsAfter) {
CurrentRecipe.AddAfterSelf(NewRecipe);
} else {
CurrentRecipe.AddBeforeSelf(NewRecipe);
}
}
您在错误的文档级别添加新节点。必须将元素添加到(如所指向的)到类别节点,而不是添加到同级节点。有两个选项可以查找正确的节点,然后将节点添加到正确的位置:
- 当您执行此操作时,遍历所有节点以查找匹配项
- 直接使用 XPath/path/element[@attribute='attributeName'] 查找节点
将节点添加到正确位置的示例如下所示:
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(@"d:tempthexmlfile.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:tempthexmlfileresult.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:tempthexmlfile.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:tempthexmlfileresult.xml");
输出是相同的。LINQ2XML在许多方面比 XmlDocument 更容易、更舒适地使用。例如,选择子节点可能会容易得多,并且您不需要 XPath 字符串:
xml.Descendants("recipe");
LINQ2XML值得一试。