I have this original Xml:
<?xml version="1.0" encoding="utf-8"?>
<Home>
<Kitchen>
<Pantry>
<Ingredients>
<Name>Tomato</Name>
<Price>
<ID>1</ID>
<Name>SALES</Name>
</Price>
<Cost>
<ID>2</ID>
<Name>COGS</Name>
</Cost>
</Ingredients>
</Pantry>
</Kitchen>
</Home>
And I want it to look like this, concatenated price/cost-id/name:
<?xml version="1.0" encoding="utf-8"?>
<Home>
<Kitchen>
<Pantry>
<Ingredients>
<Name>Tomato</Name>
<Price_ID>1</Price_ID>
<Price_Name>SALES</Price_Name>
<Cost_ID>2</Cost_ID>
<Cost_Name>COGS</Cost_Name>
</Ingredients>
</Pantry>
</Kitchen>
</Home>
Here's my code so far, and I am getting an error in this line xChild.ReplaceWith(new XElement(concatName, xChild.Value.ToString()));
-- System.NullReferenceException: 'Object reference not set to an instance of an object.'
// source programmatically generated
XElement xml = XElement.Parse(xml.ToString());
foreach (XElement xParent in xQuery.Descendants("Ingredients").Nodes())
{
if (xParent.HasElements)
{
string parentName = xParent.Name.ToString();
foreach (XElement xChild in xParent.DescendantsAndSelf(xParent.Name).Nodes())
{
string childName = xChild.Name.ToString();
string concatName = xParent.Name.ToString() "_" xChild.Name.ToString();
XElement child = xChild.Element(childName); //looks like this is the culprit
xChild.ReplaceWith(new XElement(concatName, xChild.Value.ToString())); //error here: System.NullReferenceException: 'Object reference not set to an instance of an object.'
XElement concatElement = xChild.Element(concatName);
xChild.AddBeforeSelf(concatElement);
}
foreach (XElement elParent in xQuery.Descendants("Ingredients"))
{
if (elParent.Element(parentName) != null)
{
XElement parentEl = xParent.Element(parentName);
parentEl.Remove();
}
}
}
}
CodePudding user response:
Your primary issue is that your .Nodes()
list is changing while you are enumerating it. You need to use .ToList
or .ToArray
on it.
It feels like you are over-complicating this anyway. You can just remove each child node, rename it, and add back as a sibling of its parent.
var ingredients = xQuery.Descendants("Ingredients");
foreach (XElement xParent in ingredients.Nodes().ToList())
{
if (!xParent.HasElements)
continue;
string parentName = xParent.Name.ToString();
foreach (XElement xChild in xParent.Nodes().ToArray())
{
string concatName = xParent.Name "_" xChild.Name;
xChild.Remove();
xChild.Name = concatName;
xParent.AddBeforeSelf(xChild);
}
xParent.Remove();
}