Scription
Scription

Reputation: 645

Convert first child XML node to become a parent node for all of its siblings

I need to move a specific first child node and convert it into a parent node as follows:

I have changed the XML to make it clearer.

Original XML

<root version="1.0">
    <body>
        <nodeList projectName="testing charcharistics">
            <node modelDescription="I'm parent node which needs to be removed">
                <node modelCatalogNum="I'm First Son node to become parent node" attr1="1" attr2="2" ....... />
                <node modelCatalogNum="NETDIR-1" />
                <node modelCatalogNum="NETDIR-2" />
                <node modelCatalogNum="NETDIR-3" />
                .
                .
                .
                .
            </node>
        </nodeList>
    </body>
</root>

Output XML

<root version="1.0">
    <body>
        <nodeList projectName="testing charcharistics">
            <node modelCatalogNum="I'm First Son node to become parent node" attr1="1" attr2="2" ....... />
                <node modelCatalogNum="NETDIR-1" />
                <node modelCatalogNum="NETDIR-2" />
                <node modelCatalogNum="NETDIR-3" />
                .
                .
                .
                .
            </node>
        </nodeList>
    </body>
</root>

As you can see the first child node has become a parent node.

I need a generic solution in C# code something with the following steps.

  1. First son will become the parent of all its sibling nodes
  2. remove parent node.

Can anyone help?

Upvotes: 0

Views: 959

Answers (3)

JLRishe
JLRishe

Reputation: 101730

How about this generic approach:

private static void Invert(XmlNode startingNode, string path)
{
    XmlNode node = startingNode.SelectSingleNode(path);
    if (node != null)
    {
        XmlNode firstChild = node.SelectSingleNode("*");

        if(firstChild != null)
        {
            XmlNodeList others = 
                 node.SelectNodes("node()[not(self::*)] | *[position() > 1]");
            foreach (XmlNode other in others)
            {
                firstChild.AppendChild(other);
            }
            node.ParentNode.ReplaceChild(firstChild, node);
        }
    }
}

You can call it like this:

 Invert(doc, "/root/body/nodeList/node");

Alternatively, you could use an XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="nodeList/node">
    <xsl:apply-templates select="*[1]" mode="newParent" />
  </xsl:template>

  <xsl:template match="*" mode="newParent">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="../node()[generate-id() !=
                                             generate-id(current())]" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Upvotes: 1

Lawrence Thurman
Lawrence Thurman

Reputation: 677

How about this?

XmlDocument doc = new XmlDocument();
        doc.Load("Test.xml");

you could send in a variable to determine the node to remove XmlNodeList elementList = doc.GetElementsByTagName("node");

Just to Show the list

        foreach (XmlNode node in elementList)
        {                
            Console.Write(node.Name + " ");
            foreach (XmlAttribute attr in node.Attributes)
            {
                Console.Write(attr.Name + ":" + attr.Value + " ");
            }
            Console.WriteLine();
        }

Generic removal - basically remove attributes from parent and add the ones from first child, remove the child

        var firstChild = elementList[0].FirstChild;
        elementList[0].Attributes.RemoveAll();
        for (int i = 0; i < firstChild.Attributes.Count; i++)
        {
            XmlAttribute item = doc.CreateAttribute(firstChild.Attributes[i].Name);
            item.Value = firstChild.Attributes[i].Value;
            elementList[0].Attributes.Append(item);
        }
        elementList[0].RemoveChild(firstChild);

Just to Show the list

        Console.WriteLine();
        foreach (XmlNode node in elementList)
        {                
            Console.Write(node.Name + " ");
            foreach (XmlAttribute attr in node.Attributes)
            {
                Console.Write(attr.Name + ":" + attr.Value + " ");
            }
            Console.WriteLine();
        }

        Console.ReadLine();

Upvotes: 0

Gun
Gun

Reputation: 1411

Here is the solution you can remove self closing tag separately.

Test.xml - Your original xml and Test1.xml - Modified xml

        XmlDocument doc = new XmlDocument();
        doc.Load(@"E:\Test.xml");

        XmlNodeList elementList = doc.GetElementsByTagName("node");

        for (int i = 0; i < elementList.Count; i++)
        {
            if (elementList[0].Attributes["modelDescription"].Value == "My Model Description")
            {
                elementList[0].Attributes["modelDescription"].Value = elementList[1].Attributes["modelDescription"].Value;

                XmlAttribute newAttribute1 = doc.CreateAttribute("instanceName");
                newAttribute1.Value = elementList[1].Attributes["instanceName"].Value;
                elementList[0].Attributes.Append(newAttribute1);

                XmlAttribute newAttribute2 = doc.CreateAttribute("modelCatalogNum");
                newAttribute2.Value = elementList[1].Attributes["modelCatalogNum"].Value;
                elementList[0].Attributes.Append(newAttribute2);

                elementList[1].ParentNode.Attributes.Remove(elementList[0].Attributes["quantity"]);

                elementList[0].FirstChild.RemoveAll();
            }
        }

        doc.Save(@"E:\Test1.xml");  

Upvotes: 2

Related Questions