0

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.

3 Answers 3

1

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
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, this is very useful. However I have to use an XPath expression and the structure of the XML may alter so I can't reference any element properties in code. Regardless this has helped me find a solution, so it is much appreciated!
1

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

Comments

0

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

1 Comment

I've edited the question to make it clear the XPath finds 'every dessert course of ice cream' not 'every Meal that has a dessert course of ice cream'. The XPath is correct in the question. No problem with that, my question was how to add an element in C#. Thank you anyway

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.