usefulcat
usefulcat

Reputation: 339

Add XML Element after multiple elements

I have this 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>

And this XPath expression:

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

This finds every dessert course of ice cream. I want to add a new element after each matching element like so:

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

How can I do this in C# given that XPath expression? The expression may alter (for example selecting mains instead), and the structure of the XML may vary too. Regardless a new element must be inserted after each matching element.

I know there is a AddAfterSelf method on an XElement, but I don't know how to use that on the collection of XElements the XPath expression may return.

Upvotes: 0

Views: 96

Answers (3)

Alejandro
Alejandro

Reputation: 1882

And this XPath expression:

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

This finds every Meal that has a dessert course of ice cream. I want to add a new element after each matching element like so:

That expression is wrong. You need:

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

Upvotes: 0

usefulcat
usefulcat

Reputation: 339

It's straightforward, but I needed a reminder from @Fabio that a foreach is what is necessary:

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

foodXml now has the additional enjoyed element after the matching elements

Upvotes: 1

Fabio
Fabio

Reputation: 32455

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

Upvotes: 1

Related Questions