Appending Element to XML Document

davearia

Well-known member
Joined
Jan 4, 2005
Messages
184
Hi All,

Apoligies for my absence, moved house last year.

I am trying to copy an element from an esisting XML file.

For example here is simplified version of this file:
Code:
<Ctrl Name="Customers">
    <Group GroupId="1">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
    <Group Group ="2">
         <FirstName>"Joe"</FirstName>
        <Surname>"Pope"</Surname>
    </Group >
</Ctrl >
All I am trying to do programmitically is trying to copy the element store where its ID is 1 to a new element store of ID 3 into the existing XML document, like this:
Code:
<Ctrl Name="Customers">
    <Group GroupId="1">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
    <Group Group ="2">
         <FirstName>"Jane"</FirstName>
        <Surname>"Pope"</Surname>
    </Group >
     <Group GroupId="3">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
</Ctrl >
I have not wrote much of the code as yet, all I have is this:
Code:
Dim xmlReader As New XmlTextReader("C:\work\homeTest\source\test.xml")
Dim xmlDoc As New XmlDocument
xmlDoc.Load(xmlReader)
As I understand the object XmlDocument is the correct choice for ths task. But I cannot fathom how to firstly access the element I want and the to iterate through all of this elements nodes and then create this new element in the existing xml document.

Please help?

Thanks, Dave.
 
To append an element to XmlDocument you have to go thru these three simple steps:

* Find a place where exactly you want new element to be place.
This can be done using XPath queries (this it the preferred way -
see SelectSingleNode method in MSDN) or by iterating thru XmlDocuments
object model (which is only reasonable in case of small documents with
simple structure). As a result of your search you would have a reference
to XmlNode object - the future parent for new to-be-created node.

* Find a node you want to make a copy. When that node is found - clone it by
using, comes with no surprise, Clone method.

* Append duplicate (created at the previous step) to the parent node (found at
first step) using AppendChild method.

Thats all.

ps: and dont forget to save changed XmlDocument.


davearia said:
Hi All,

Apoligies for my absence, moved house last year.

I am trying to copy an element from an esisting XML file.

For example here is simplified version of this file:
Code:
<Ctrl Name="Customers">
    <Group GroupId="1">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
    <Group Group ="2">
         <FirstName>"Joe"</FirstName>
        <Surname>"Pope"</Surname>
    </Group >
</Ctrl >
All I am trying to do programmitically is trying to copy the element store where its ID is 1 to a new element store of ID 3 into the existing XML document, like this:
Code:
<Ctrl Name="Customers">
    <Group GroupId="1">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
    <Group Group ="2">
         <FirstName>"Jane"</FirstName>
        <Surname>"Pope"</Surname>
    </Group >
     <Group GroupId="3">
        <FirstName>"Joe"</FirstName>
        <Surname>"Soap"</Surname>
    </Group >
</Ctrl >
I have not wrote much of the code as yet, all I have is this:
Code:
Dim xmlReader As New XmlTextReader("C:\work\homeTest\source\test.xml")
Dim xmlDoc As New XmlDocument
xmlDoc.Load(xmlReader)
As I understand the object XmlDocument is the correct choice for ths task. But I cannot fathom how to firstly access the element I want and the to iterate through all of this elements nodes and then create this new element in the existing xml document.

Please help?

Thanks, Dave.
 
Thats All???

I appreciate the overview..... but it caught me funny... I have been searching on how to accomplish something like this for hours....

I have seen hundreds of post and questions all over the internet where people respond with overviews and search this method or that method but no examples whatsoever.

It took me 4 days to finannly figure out how to update an sml file using textboxes... wow is it simple, however..... finding it is like pulling teeth most undesirable.

I can find all sort of examples that are no were near what I am needing done.... or what this guy is needing done...

Well at least you tried to give input....

Dont get me wrong not coming down on you .... just got tickled since I have been searching for a few hours myself...

Well back to the search ....

Oh for the thread starter I hope you find what your looking for, if you do please post it cause I am looking too.

I need something simular..


I have a structure like this


,
Code:
<ChurchMembership>
  <Members>
    <Name>Jim Bob</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
  <Members>
    <Name>testing again and again</Name>
    <Phone>(098) 765-4321</Phone>
    <Address>well anything yet?</Address>
  </Members>
  <Members>
    <Name>Johnny Boy</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
  <Members>
    <Name>testing again and again</Name>
    <Phone>(098) 765-4321</Phone>
    <Address>well anything yet?</Address>
  </Members>
  <Members>
    <Name>Franky</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
  <Members>
    <Name>Number 6</Name>
    <Phone>(123) 456-9878</Phone>
    <Address>THIS SHOULD LAND AS NUMBER SIX</Address>
  </Members>
    New REcord added here I hope
</ChurchMembership>


ANyway..... I am simply trying to add this at the bottom

Code:
<Members>
    <Name>Number 7</Name>
    <Phone>123-456-7890</Phone>
    <Address>THIS SHOULD LAND AS NUMBER SEVEN</Address>
  </Members>

Any chance you all have an idea how to do it or where its done I would love to know....
 
Here is a snippet of code I ended up using if it helps at all:
Code:
 Dim xmlNodelst2 As XmlNodeList = xmlDoc.SelectNodes("//Firstname")
 For Each xmlItem As XmlNode In xmlNodelst2
      Dim storeId As Int32 = 0
      If Not xmlItem.Attributes("Firstname") Is Nothing Then
          idType = "StoreGroupId"
          Int32.TryParse(xmlItem.Attributes("Firstname").Value, storeId)
          If storeId = 1 Then
               StoreUKXMLNode = xmlItem
               Exit For
          End If
      End If
Next
If Not StoreUKXMLNode Is Nothing Then
    Create Store3 Element
    Dim StoreBizElement As XmlElement
    StoreBizElement = xmlDoc.CreateElement(StoreUKXMLNode.Name)
    Dim StoreIdAttr As XmlAttribute
    StoreIdAttr = xmlDoc.CreateAttribute(idType)
    StoreIdAttr.Value = 3
    StoreBizElement.Attributes.Append(StoreIdAttr)
    StoreBizElement.InnerXml = StoreUKXMLNode.InnerXml
    StoreUKXMLNode.ParentNode.AppendChild(StoreBizElement)
End If
 
vbMarkO this will suit your purposes....
C#:
// get the original document
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.Load(@"C:\input.xml");

// create a new node
System.Xml.XmlNode memberNode = xmlDoc.CreateElement("Members");

// create the child nodes
System.Xml.XmlNode nameNode = xmlDoc.CreateElement("Name");
nameNode.InnerText = "Peter";
System.Xml.XmlNode phoneNode = xmlDoc.CreateElement("Phone");
phoneNode.InnerText = "01664 123456";
System.Xml.XmlNode addressNode = xmlDoc.CreateElement("Address");
addressNode.InnerText = "This is my address";

// add the child nodes to the new node
memberNode.AppendChild(nameNode);
memberNode.AppendChild(phoneNode);
memberNode.AppendChild(addressNode);

// add the new node to the file and save
xmlDoc.FirstChild.AppendChild(memberNode);
xmlDoc.Save(@"C:\output.xml");
Code:
         get the original document
        Dim xmlDoc As New System.Xml.XmlDocument
        xmlDoc.Load("C:\input.xml")

         create a new node
        Dim memberNode As System.Xml.XmlNode = xmlDoc.CreateElement("Members")

         create the child nodes
        Dim nameNode As System.Xml.XmlNode = xmlDoc.CreateElement("Name")
        nameNode.InnerText = "Peter"
        Dim phoneNode As System.Xml.XmlNode = xmlDoc.CreateElement("Phone")
        phoneNode.InnerText = "01664 123456"
        Dim addressNode As System.Xml.XmlNode = xmlDoc.CreateElement("Address")
        addressNode.InnerText = "This is my address"

         add the child nodes to the new node
        memberNode.AppendChild(nameNode)
        memberNode.AppendChild(phoneNode)
        memberNode.AppendChild(addressNode)

         add the new node to the file and save
        xmlDoc.FirstChild.AppendChild(memberNode)
        xmlDoc.Save("C:\output.xml")
Hope the vb is right im a little rusty
 
Thanx this works great but to get it to work I had to make one change...

Code:
xmlDoc.DocumentElement.AppendChild(memberNode)  DocumentElement


xmlDoc.FirstChild.AppendChild(memberNode)   instead of FirstChild

It works great..... Also I love the simple understandable approach... and I need that approach LOL

Anyway, would it be to much to ask if you just as simple of an approach to read it back out to textboxes....

For instance txtName.text, txtPhone.text, txtAddress.text????

I have found one way to do this byval of posistion as integer

As I understand it it iterates through the nodes by index posistion if index is the right word to describe it...

But I would be very interested in your approach to this!!!

vbMarkO
 
Well this has certainly been a refresher for me, I havent used the DOM (XmlDocument class) in a long time, because it becomes extremely inefficient and resource intensive with large xml files. Reading it back out is a simple matter of reading the InnerText property of the right nodes.
Code:
 once you have the right Members node you can just do
txtName.Text = myNode.ChildNodes(0)
txtPhone.Text = myNode.ChildNodes(1)
txtAddress.Text = myNode.ChildNodes(2)
Obviously this has some limitations in as much as it wont work correctly if you re-order the nodes within a Members node. It would be possible to create a slightly more effective system by looping through the childnodes of a Members node, and checking if the Name property is the one your looking for before retrieving the value.

It will be difficult to help you much better without knowing exactly how you are specifying which Members node you wish to retrieve. Moving between the Members nodes can be as simple as a for each loop, but as I said helping much further would require knowing how you plan on stopping at a specific node.
Code:
        Dim myDoc As System.Xml.XmlDocument
        myDoc.Load("c:\Test.xml")

         An example of a conditional search (albiet a poor one)
         the code will return the values from the first Members 
         node with the name bob
        For Each myNode As System.Xml.XmlNode In myDoc.DocumentElement.ChildNodes
            If myNode.FirstChild.InnerText = "Bob" Then
                txtName.Text = myNode.ChildNodes(0)
                txtPhone.Text = myNode.ChildNodes(1)
                txtAddress.Text = myNode.ChildNodes(2)
                Exit For
            End If
        Next
 
This is great stuff.....

What I would have done to find a thread like this a week ago.

The code works great thanx for pointing me in the right direction.
Since we are on a roll think we could do one more? :)
Now that I can append a node and its children I am looking for how to delete that node and its children.



vbMarkO
 
Code:
        Dim myDoc As New System.Xml.XmlDocument
        myDoc.Load("c:\input.xml")

        For Each myNode As System.Xml.XmlNode In myDoc.DocumentElement.ChildNodes
            If myNode.FirstChild.InnerText = "Bob" Then
                myNode.ParentNode.RemoveChild(myNode)
                Exit For
            End If
        Next

        myDoc.Save("c:\output.xml")
 
Awesome Guys.... this has really been an eye opener for me.... no wonder everyon has been trying to tell me to use xml instead of textfiles....

I have a question if I might......

About the following code

Code:
Dim xmlDoc As New xmlDocument
xmlDoc.Load(xmlFile)

Does this ever need to be closed?

Should this only be opened once? or can it be loaded in each button click event such as

Savexml event
DeleteNode event
update event and so on?

vbMarkO
 
Oddly the XmlDocument class doesnt implement IDisposable, which seems strange for a class that seems to be a resource hog. It doesnt even have a Close method. You could assign the class to null after your finished with it, but with my limited understanding I have no idea if this will help the Garbage Collector clear up the resources any quicker.

As for whether to open the file only once or multiple times is a mixed bag. Loading the file can take time, as such if you are likely to be performing many operations on it, it would make sense to keep the file open. The XmlDocument class can however use alot of memory so you could argue that these resources should be freed up when not being used. The only problem with this is that since there is no Dispose method, there is no clear way todo this.

Personally most of the problems Ive had with resources have been when I was using very large xml files (im talking 30MB). As long as you are using smallish files, I see no reason why you cant just keep the file open and make changes whenever you want, this will save you waiting for the file to be opened before making the next change.
 
Dispose is only required when an object holds onto non-managed resources (think file handles, db connections etc.) as these need an immediate release when no longer required.
Even though the XmlDocument class can be a memory hog its just memory and therefore the GC knows best ;)
 
This being the case is it advisable to set the object equal to null thus removing all references to it, helping the GC to realise it is no longer in use or is this not required? Assumably an object created by a method would become null as soon as the method was through anyway, but Im talking more about if its a public level object that is no longer needed.
 
If you are doing a release build you dont need to set it to null for objects within a method, the GC will free resources that are no longer used even if it triggers mid function.

For class level variables then if you no longer need the object setting it to null will allow the GC to free it.
 
That application in this case or xmlfile is very small.... it will only be holding 100 + names and personal info as addres phones and such as it is an addressbook

Let me ask this then...

Perhaps I should open it do what is needed then when I close the app will this then free or act as though the xmlDoc has been closed or are those resources still being used even after the app is closed...

vbMarkO
 
Basically what PlausiblyDamp was explaining to me is that it will clear up its own resources. If you close the application the Garbage Collector will handle it for you. The only time you can really do anything is if you have an XmlDocument declared at class level (ie. public XmlDocument myDocument; at the top of your class) that you will no longer need. If this is the case you can assign it to null (myDocument = null;) to let the GC know your done with it.
 
Great, sounds good.

Thank you guys for all the input here it has been some of the best I have found online.

Learned alot from this thread.

vbMarkO
 
Consider serialization

Now that youve found a satisfactory solution, Im just going to jump in here and add my "two cents".

When I first started working with XML documents in .Net, I too approached it by reading, deleting, and inserting individual nodes "manually". However, once the complexity of your XML document design passes a certain point, I find this approach can be difficult to maintain, and tightly links your code to the XML schema, so even relatively small changes in the schema can require major code changes.

The method I now use is to create an XML schema document (XSD) which defines very strictly how the XML is arranged, and then use the VS xsd tool to generate classes based on the schema. Then it is a simple matter of using the XmlSerializer class to instantiate a graph of useable objects from the XML, and later serialize the objects back into XML.

Besides the initial creation of the XSD, this approach requires a minimal amount of programming and maximum flexibility. And resource usage wise, I would expect it to be pretty efficient too, especially if there are a large number of insert and delete operations.

Of course for the time being neither of you (davearia and vbMarkO) need to worry about this, as your document models are both fairly simple, but if you ever work with XML on a larger scale, I would recommend you look into serialization.

:cool:
 
Thanx for the 2 cents I will certainly take that into consideration as I learn more about xml....

Ok, just found a problem too..... it is in reguard to the update code I got here...

Note: It works perfect until I sort the listbox..... since this code updates the xml node based on posistion by the index of the node..... when I use the selectedIndex of the lstNames listbox the sorted names no longer are in the same index sequence.

The listbox is in sort order but the xmlfile is not.

So I need to modify this code so that it updates somehow based on the selectedItem of the listbox(lstNames) and not selectedIndex...

Here is the code as I have it... NOTE again if I change the sort property of the listbox this code works great for updating my records....

So how can we change it

Code:
  xmlDoc.Load(xmlFile)
        Dim ChurchMembership As XmlNode = xmlDoc.ChildNodes(1)

        Dim i As Integer = lstNames.SelectedIndex  <-- This is the problem lstNames is now sorted so
         the index doesnt match and is overwriting other nodes... instead of updating the right one


         //////////////// TODO:  This block of code is not working properly \\\\\\\\\
        ChurchMembership.ChildNodes(i).ChildNodes(0).InnerText = txtName.Text
        ChurchMembership.ChildNodes(i).ChildNodes(1).InnerText = txtPhone.Text
        ChurchMembership.ChildNodes(i).ChildNodes(2).InnerText = txtAddress.Text
        ChurchMembership.ChildNodes(i).ChildNodes(3).InnerText = cboCity.Text
        ChurchMembership.ChildNodes(i).ChildNodes(4).InnerText = txtState.Text
        ChurchMembership.ChildNodes(i).ChildNodes(5).InnerText = txtZip.Text
        ChurchMembership.ChildNodes(i).ChildNodes(6).InnerText = lstDates.Items.Add("No Dates available")
        ChurchMembership.ChildNodes(i).ChildNodes(7).InnerText = txtNote.Text
        xmlDoc.Save(xmlFile)

Here again is a smaple of my xmlFIle structure

Code:
<ChurchMembership>
  <Members>
    <Name>Jim Bob</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
  <Members>
    <Name>John Hancock</Name>
    <Phone>(098) 765-4321</Phone>
    <Address>Sometown</Address>
  </Members>
  <Members>
    <Name>Johnny Boy</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
  <Members>
    <Name>Mary Jane</Name>
    <Phone>(123) 456-4321</Phone>
    <Address>well anything yet?</Address>
  </Members>
  <Members>
    <Name>Franky</Name>
    <Phone>(123) 456-7890</Phone>
    <Address>Not a Cowboy</Address>
  </Members>
</ChurchMembership>
 
The neatest solution would actually use MrPauls suggestion. The idea is you create a class called Person. This class stores all the information about a person, including name, address etc. You then have a collection of these classes. Whenever you make changes to one of the people, you update their class. In order to store the data to file you can then Serialise and DeSerialise the file. Now I can guide you through an example of this if you require and Im sure MrPaul can provide a more elegant solution (I have no experience of using an XSD).
 
Back
Top