davearia Posted July 24, 2006 Posted July 24, 2006 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: <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 it's ID is 1 to a new element store of ID 3 into the existing XML document, like this: <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: 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. Quote
Igor Sukhov Posted July 25, 2006 Posted July 25, 2006 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. That's all. ps: and don't forget to save changed XmlDocument. 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: <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 it's ID is 1 to a new element store of ID 3 into the existing XML document, like this: <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: 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. Quote
vbMarkO Posted July 26, 2006 Posted July 26, 2006 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 ,<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 <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.... Quote Visual Basic 2008 Express Edition!
davearia Posted July 30, 2006 Author Posted July 30, 2006 Here is a snippet of code I ended up using if it helps at all: 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 Quote
Cags Posted July 30, 2006 Posted July 30, 2006 vbMarkO this will suit your purposes.... // 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"); ' 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 i'm a little rusty Quote Anybody looking for a graduate programmer (Midlands, England)?
vbMarkO Posted July 31, 2006 Posted July 31, 2006 Thanx this works great but to get it to work I had to make one change... 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 Quote Visual Basic 2008 Express Edition!
Cags Posted July 31, 2006 Posted July 31, 2006 Well this has certainly been a refresher for me, I haven't 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. ' 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 won't 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. 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 Quote Anybody looking for a graduate programmer (Midlands, England)?
vbMarkO Posted July 31, 2006 Posted July 31, 2006 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 Quote Visual Basic 2008 Express Edition!
Cags Posted July 31, 2006 Posted July 31, 2006 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") Quote Anybody looking for a graduate programmer (Midlands, England)?
vbMarkO Posted August 1, 2006 Posted August 1, 2006 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 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 Quote Visual Basic 2008 Express Edition!
Cags Posted August 1, 2006 Posted August 1, 2006 Oddly the XmlDocument class doesn't implement IDisposable, which seems strange for a class that seems to be a resource hog. It doesn't 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 I've had with resources have been when I was using very large xml files (i'm talking 30MB). As long as you are using smallish files, I see no reason why you can't 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. Quote Anybody looking for a graduate programmer (Midlands, England)?
Administrators PlausiblyDamp Posted August 1, 2006 Administrators Posted August 1, 2006 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 it's just memory and therefore the GC knows best ;) Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
Cags Posted August 1, 2006 Posted August 1, 2006 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 I'm talking more about if its a public level object that is no longer needed. Quote Anybody looking for a graduate programmer (Midlands, England)?
Administrators PlausiblyDamp Posted August 1, 2006 Administrators Posted August 1, 2006 If you are doing a release build you don't 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. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
vbMarkO Posted August 1, 2006 Posted August 1, 2006 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 Quote Visual Basic 2008 Express Edition!
Cags Posted August 1, 2006 Posted August 1, 2006 Basically what PlausiblyDamp was explaining to me is that it will clear up it's 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. Quote Anybody looking for a graduate programmer (Midlands, England)?
vbMarkO Posted August 2, 2006 Posted August 2, 2006 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 Quote Visual Basic 2008 Express Edition!
MrPaul Posted August 2, 2006 Posted August 2, 2006 Consider serialization Now that you've found a satisfactory solution, I'm 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: Quote Never trouble another for what you can do for yourself.
vbMarkO Posted August 2, 2006 Posted August 2, 2006 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 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 <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> Quote Visual Basic 2008 Express Edition!
Cags Posted August 2, 2006 Posted August 2, 2006 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 I'm sure MrPaul can provide a more elegant solution (I have no experience of using an XSD). Quote Anybody looking for a graduate programmer (Midlands, England)?
Administrators PlausiblyDamp Posted August 2, 2006 Administrators Posted August 2, 2006 Basic sample class... Public Class Member Private _Name As String Private _Phone As String Private _Address As String Public Sub New() End Sub Public Sub New(ByVal name As String, ByVal phone As String, ByVal address As String) _Name = name _Phone = phone _Address = address End Sub Public Property Name() As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Public Property Phone() As String Get Return _Phone End Get Set(ByVal value As String) _Phone = value End Set End Property Public Property Address() As String Get Return _Address End Get Set(ByVal value As String) _Address = value End Set End Property End Class Sample code to populate / save list... Dim members As New List(Of Member) 'for .net 2 use an array or arraylist for .net 1 members.Add(New Member("Jim Bob", "(123) 456-7890", "Not a cowboy")) members.Add(New Member("John Hancock", "(098) 765-4321", "Sometown")) members.Add(New Member("etc.", "etc.", "etc.")) Dim xs As New Xml.Serialization.XmlSerializer(GetType(List(Of Member))) Dim fs As New System.IO.FileStream("c:\test.xml", IO.FileMode.Create, IO.FileAccess.ReadWrite) xs.Serialize(fs, members) fs.Close() Reading it back in... Dim xs2 As New Xml.Serialization.XmlSerializer(GetType(List(Of Member))) Dim fs2 As New System.IO.FileStream("c:\test.xml", IO.FileMode.Open, IO.FileAccess.Read) Dim m As List(Of Member) m = DirectCast(xs2.Deserialize(fs2), List(Of Member)) fs2.Close() hope that gets you started. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
vbMarkO Posted August 3, 2006 Posted August 3, 2006 Wow, It sounds complicated but I need it and if its the only way to get it done then I am up to learning how.... vbMarkO Quote Visual Basic 2008 Express Edition!
vbMarkO Posted August 3, 2006 Posted August 3, 2006 You must have posted that near the same time I was posting earlier... Again WOW thats going to take some breaking down .... I will certainly dive into this to try and understand it so I can recreate it later if needed. However while you were writing this out, I went to figuring about how I did the save and also the remove and it hit me I could remove and save a newrecord at the same time simulating an update of sorts... Crude but it works :) I still cant help but feel if I can search and find the exact node to remove I should be able to update more efficiently than this.... Here is my crude method of updating this small addressbook Dim xmlDoc As New XmlDocument xmlDoc.Load(xmlFile) For Each myNode As XmlNode In xmlDoc.DocumentElement.ChildNodes If myNode.FirstChild.InnerText = lstNames.SelectedItem Then myNode.ParentNode.RemoveChild(myNode) Exit For End If Next xmlDoc.Save(xmlFile) '///////////////////////////////// ' 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 = txtName.Text Dim phoneNode As System.Xml.XmlNode = xmlDoc.CreateElement("Phone") phoneNode.InnerText = txtPhone.Text '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.DocumentElement.AppendChild(memberNode) xmlDoc.Save(xmlFile) Call DisplayRecords() Creative I think :) Dont worry Im not satisfied just yet.... There has got to be a way to iterate through find the node block that needs to be updated and update it... so I am a searching vbMarkO Quote Visual Basic 2008 Express Edition!
Cags Posted August 3, 2006 Posted August 3, 2006 It's certainly possible to iterate through and find the person you wish to update, but there is one major issue with your current system that could cause a problem. You use the name to search, but the chances are in even a relatively small system there will be more than one person with the same name. Using an iterative system would be far easier if you had a unique ID for each person. Quote Anybody looking for a graduate programmer (Midlands, England)?
vbMarkO Posted August 3, 2006 Posted August 3, 2006 Sure I understand that and certainly agree with you. Though in this case at the moment there are no duplicate names within this group though it is certianly possible there could be done the road. Not sure how to make a UNique ID ... I mean I know we could use SS# but thats to personal for this group as there is no need for me to have access to that info... Phone Number isnt specific enough.... So Im not sure what I would use But I would love to see how I might iterate this as you mentioned even with this potential problem and see how I might iterate through and then update vbMarkO Quote Visual Basic 2008 Express Edition!
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.