Добавить элемент XML после нескольких элементов

#c# #xml #xpath

#c# #xml #xpath

Вопрос:

У меня есть этот XML:

 <Meals>
    <Meal>
        <Food course="starter">Soup</Food>
        <Food course="mains">Fish</Food>
        <Food course="dessert">Ice cream</Food>
    </Meal>
    <Meal>
        <Food course="starter">Soup</Food>
        <Food course="mains">Chicken</Food>
        <Food course="dessert">Cheese</Food>
    </Meal>
    <Meal>
        <Food course="starter">Melon</Food>
        <Food course="mains">lamb</Food>
        <Food course="dessert">Ice cream</Food>
    </Meal>
</Meals>
  

И это выражение XPath:

 //Food[@course='dessert' and text() = 'Ice cream']
  

Это находит каждое десертное блюдо из мороженого. Я хочу добавить новый элемент после каждого соответствующего элемента следующим образом:

 <Meals>
    <Meal>
        <Food course="starter">Soup</Food>
        <Food course="mains">Fish</Food>
        <Food course="dessert">Ice cream</Food>
        <Review>enjoyed</Food>
    </Meal>
    <Meal>
        <Food course="starter">Soup</Food>
        <Food course="mains">Chicken</Food>
        <Food course="dessert">Cheese</Food>
    </Meal>
    <Meal>
        <Food course="starter">Melon</Food>
        <Food course="mains">lamb</Food>
        <Food course="dessert">Ice cream</Food>
        <Review>enjoyed</Food>
    </Meal>
</Meals>
  

Как я могу это сделать на C #, учитывая это выражение XPath? Выражение может измениться (например, вместо этого выбрать mains), и структура XML также может измениться. Независимо от этого, новый элемент должен быть вставлен после каждого соответствующего элемента.

Я знаю, что в XElement есть метод AddAfterSelf, но я не знаю, как использовать его в коллекции XElements, которую может возвращать выражение XPath.

Ответ №1:

LINQ для XML

 var doc = XDocument.Parse("filepath");

var enjoyedMeals = 
   doc.Descendants("Meal")
      .Where(meal => meal.Elements.Any(food => food.Attribute("course").Value == "desert"));

foreach (var meal in enjoyedMeals)
{
    var review = new XElement("Review", "enjoyed");
    meal.Elements.Add(review);
}

doc.ToString(); // contains <Review> elements
  

Комментарии:

1. Спасибо, это очень полезно. Однако мне приходится использовать выражение XPath, и структура XML может измениться, поэтому я не могу ссылаться на какие-либо свойства элемента в коде. Несмотря на это, это помогло мне найти решение, поэтому я очень ценю!

Ответ №2:

Это просто, но мне нужно было напоминание от @Fabio о том, что foreach — это то, что необходимо:

 foreach (var element in foodXml.XPathSelectElements(xpathExpression))
{
    element.AddAfterSelf(new XElement($"Review", "enjoyed"));
}
  

В foodXml теперь есть дополнительный элемент enjoyed после соответствующих элементов

Ответ №3:

И это выражение XPath:

 //Food[@course='dessert' and text() = 'Ice cream']
  

Это находит каждое блюдо, в котором есть десертное блюдо из мороженого. Я хочу
чтобы добавить новый элемент после каждого соответствующего элемента, вот так:

Это выражение неверно. Вам нужно:

 /Meals/Meal[Food[@course = 'dessert'] = 'Ice cream']
  

Комментарии:

1. Я отредактировал вопрос, чтобы было ясно, что XPath находит «каждое десертное блюдо из мороженого», а не «каждое блюдо, в котором есть десертное блюдо из мороженого». В вопросе указан правильный XPath. С этим проблем нет, мой вопрос заключался в том, как добавить элемент в C #. В любом случае спасибо