18

I have a huge bunch of XML files with the following structure:

<Stuff1>
  <Content>someContent</name>
  <type>someType</type>
</Stuff1>
<Stuff2>
  <Content>someContent</name>
  <type>someType</type>
</Stuff2>
<Stuff3>
  <Content>someContent</name>
  <type>someType</type>
</Stuff3>
...
...

I need to change the each of the "Content" node names to StuffxContent; basically prepend the parent node name to the content node's name.

I planned to use the XMLDocument class and figure out a way, but thought I would ask if there were any better ways to do this.

8 Answers 8

61

(1.) The [XmlElement / XmlNode].Name property is read-only.

(2.) The XML structure used in the question is crude and could be improved.

(3.) Regardless, here is a code solution to the given question:

String sampleXml =
  "<doc>"+
    "<Stuff1>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff1>"+
    "<Stuff2>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff2>"+
    "<Stuff3>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff3>"+
  "</doc>";

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(sampleXml);

XmlNodeList stuffNodeList = xmlDoc.SelectNodes("//*[starts-with(name(), 'Stuff')]");

foreach (XmlNode stuffNode in stuffNodeList)
{
    // get existing 'Content' node
    XmlNode contentNode = stuffNode.SelectSingleNode("Content");

    // create new (renamed) Content node
    XmlNode newNode = xmlDoc.CreateElement(contentNode.Name + stuffNode.Name);

    // [if needed] copy existing Content children
    //newNode.InnerXml = stuffNode.InnerXml;

    // replace existing Content node with newly renamed Content node
    stuffNode.InsertBefore(newNode, contentNode);
    stuffNode.RemoveChild(contentNode);
}

//xmlDoc.Save

PS: I came here looking for a nicer way of renaming a node/element; I'm still looking.

Sign up to request clarification or add additional context in comments.

4 Comments

It's a shame that someone with 51 rep understands this better than someone with 31k rep. +1 for you, even if it is a slightly more complex solution than I was hoping for.
It does not affect the asker's example, but for completeness your routine should not just copy across InnerXml, it should also copy any attributes: for (int i = contentNode.Attributes.Count - 1; i >= 0; i --) { newNode.Attributes.Prepend((XmlAttribute)contentNode.RemoveAttributeAt(i)); }
I'm guessing this will not work if you try to change the name of the documentElement. Which is what I'm looking for.
If you're looking to rename the document element, something like this might work for you: XmlDocument oldDoc = new XmlDocument(); oldDoc.LoadXml(myOldXmlDoc); string strNewXml= "<NewXmlRoot>" + oldDoc.DocumentElement.InnerXml + "</NewXmlRoot>"; XmlDocument newDoc= new XmlDocument(); newDoc.LoadXml(strNewXml);
6

I used this method to rename the node:

/// <summary>
/// Rename Node
/// </summary>
/// <param name="parentnode"></param>
/// <param name="oldname"></param>
/// <param name="newname"></param>
private static void RenameNode(XmlNode parentnode, string oldChildName, string newChildName)
{
    var newnode = parentnode.OwnerDocument.CreateNode(XmlNodeType.Element, newChildName, "");
    var oldNode = parentnode.SelectSingleNode(oldChildName);

    foreach (XmlAttribute att in oldNode.Attributes)
        newnode.Attributes.Append(att);
    foreach (XmlNode child in oldNode.ChildNodes)
        newnode.AppendChild(child);

    parentnode.ReplaceChild(newnode, oldNode);
}

Comments

2

The easiest way I found to rename a node is:

xmlNode.InnerXmL = newNode.InnerXml.Replace("OldName>", "NewName>")

Don't include the opening < to ensure that the closing </OldName> tag is renamed as well.

3 Comments

Probably doesn't work with empty element tags, e.g. <OldName />. But renames nested elements with the same name. And elements with the old name as a suffix, e.g <VeryOldName>..</VeryOldName>. :-(
There is not even space here to describe all the things wrong with this approach. Maybe read this question.
Maybe is a "quick and dirty" aproach but it has just save me a bunch of time of coding
1

I am not an expert in XML, and in my case I just needed to make all tag names in a HTML file to upper case, for further manipulation in XmlDocument with GetElementsByTagName. The reason I needed upper case was that for XmlDocument the tag names are case sensitive (since it is XML), and I could not guarantee that my HTML-file had consistent case in the tag names.

So I solved it like this: I used XDocument as an intermediate step, where you can rename elements (i.e. the tag name), and then loaded that into a XmlDocument. Here is my VB.NET-code (the C#-coding will be very similar).

Dim x As XDocument = XDocument.Load("myFile.html")
For Each element In x.Descendants()
  element.Name = element.Name.LocalName.ToUpper()
Next
Dim x2 As XmlDocument = New XmlDocument()
x2.LoadXml(x.ToString())

For my purpose it worked fine, though I understand that in certain cases this might not be a solution if you are dealing with a pure XML-file.

Comments

0

Perhaps a better solution would be to iterate through each node, and write the information out to a new document. Obviously, this will depend on how you will be using the data in future, but I'd recommend the same reformatting as FlySwat suggested...

<stuff id="1">
    <content/>
</stuff>

I'd also suggest that using the XDocument that was recently added would be the best way to go about creating the new document.

Comments

0

I'll answer the higher question: why are you trying this using XmlDocument?

I Think the best way to accomplish what you aim is a simple XSLT file
that match the "CONTENTSTUFF" node and output a "CONTENT" node...

don't see a reason to get such heavy guns...

Either way, If you still wish to do it C# Style,
Use XmlReader + XmlWriter and not XmlDocument for memory and speed purposes. XmlDocument store the entire XML in memory, and makes it very heavy for Traversing once...

XmlDocument is good if you access the element many times (not the situation here).

Comments

-4

Load it in as a string and do a replace on the whole lot..

    String sampleXml =
  "<doc>"+
    "<Stuff1>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff1>"+
    "<Stuff2>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff2>"+
    "<Stuff3>"+
      "<Content>someContent</Content>"+
      "<type>someType</type>"+
    "</Stuff3>"+
  "</doc>"; 

    sampleXml = sampleXml.Replace("Content","StuffxContent")

1 Comment

Bad solution, and the 'x' was clearly a placeholder in the initial question to refer to whichever numbered Stuff node was this Content node's parent.
-34

The XML you have provided shows that someone completely misses the point of XML.

Instead of having

<stuff1>
   <content/>
</stuff1>

You should have:/

<stuff id="1">
    <content/>
</stuff>

Now you would be able to traverse the document using Xpath (ie, //stuff[id='1']/content/) The names of nodes should not be used to establish identity, you use attributes for that.

To do what you asked, load the XML into an xml document, and simply iterate through the first level of child nodes renaming them.

PseudoCode:

foreach (XmlNode n in YourDoc.ChildNodes)
{        
    n.ChildNode[0].Name = n.Name + n.ChildNode[0].Name;
}

YourDoc.Save();

However, I'd strongly recommend you actually fix the XML so that it is useful, instead of wreck it further.

7 Comments

Thanks for your answer! The XML has a very different (and more complicated) schema than what I showed in my question. I was trying to simplify it for the question :).
I'm failing to understand why this is marked as correct, since as DeepBlue says below, the Name property is read only. Fascinating that it received 11 upvotes...
I agree. Have you ever had to work with any XML from Apple? It's all like this and an utter pain to parse...
@sundeep You should really reconsider to unmark this answer as correct. It just isn't
If you have 20k, know the technology and know that the answer is essentially bad, please vote to delete.
|

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.