Home > database >  Replace child name by concatenating its parent name, move up at level of parent, and remove the pare
Replace child name by concatenating its parent name, move up at level of parent, and remove the pare

Time:07-25

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();
}

dotnetfiddle

  • Related