Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Python (http://www.programmingforums.org/forum43.html)
-   -   XML minidom... help (http://www.programmingforums.org/showthread.php?t=13308)

MR.T Jun 8th, 2007 2:02 PM

XML minidom... help
 
I have been programming in python off and on and I am trying to really sit down and get more proficient in it this summer.

Right now I am trying to make a basic program for reading, modifying and writing data with an XML file. I am using minidom and I and here is the problem I am running into. Say I have a game inventory or something like this:

:

<Inv>
        <Sword>
                <Name>Broad Sword</Name>
        </Sword>
        <Shield>
                <Name>Big Shield</Name>
        </Shield>
</Inv>


I want to return the Name section of Shield. If I use a function like "getElementsByTagName" then it will return a list containing the contents of all Name sections. How do I explicitly tell minidom to get an element in... say this order Inv>Shield>Name? I tried setting it up so it loaded the elements in that order i.e I loaded Inv into a variable then I loaded Shield and finally Name. That works fine if the document is small, but obviously that's inefficient, and I cant save the file if I choose to modify an element.

I'm sure there is something simple that I am forgetting or that I missed(there always seems to be in python). Can someone please steer me in the right direction.

Arevos Jun 9th, 2007 9:03 AM

:

  1. import xml.dom.minidom
  2. xml = xml.dom.minidom.parseString("""<Inv>
  3. ...    <Sword>
  4. ...            <Name>Broad Sword</Name>
  5. ...    </Sword>
  6. ...    <Shield>
  7. ...            <Name>Big Shield</Name>
  8. ...    </Shield>
  9. ... </Inv>""")
  10.  
  11. # Assume only one inventory
  12. inv = xml.getElementsByTagName("Inv")[0]
  13.  
  14. # Get all shields in inventory
  15. shields = inv.getElementsByTagName("Shield")
  16.  
  17. # For each shield, print out its name
  18. for shield in shields:
  19.     # Assume shield has only one name
  20.     name = shield.getElementsByTagName("Name")[0]
  21.  
  22.     # Assume name tag just has text in it
  23.     print name.firstChild.wholeText

Generally speaking though, there's easier ways of storing human-readable game data in Python, unless you happen to want your data to be cross-platform.

MR.T Jun 9th, 2007 1:19 PM

Well what you posted is essentially what I did do read data from an XML file. However my problem really is about modifying the file. Say I want to change the "Broad Sword" to "Short Sword" or add another element to my XML file. I could do it that way, but wouldn't the changes just take place to the code that was in the variable? How do I modify and save the document.

Arevos Jun 9th, 2007 5:03 PM

Well, say you have found the element you want via the approach demonstrated above, and assigned it to the variable "sword":
:

  1. # Get name element of sword
  2. name = sword.getElementsByTagName("Name")[0]
  3.  
  4. # Change text in name
  5. name.firstChild.replaceWholeText("Short Sword")
  6.  
  7. # Output the new xml
  8. print xml.toxml()

This demonstrates that one can alter the XML DOM tree in place, and then output it as XML with all the changes made.

However, this isn't a very good way of doing things. A better way would be to have a "dump" and "load" method on each class:
:

  1. import xml.dom.minidom as dom
  2.  
  3. class Sword:
  4.     def __init__(self, name):
  5.         self.name = name
  6.  
  7.     def dump(self):
  8.         sword = dom.Element("Sword")
  9.         name = dom.Element("Name")
  10.         text = dom.Text()
  11.         text.replaceWholeText(self.name)
  12.         name.appendChild(text)
  13.         sword.appendChild(name)
  14.         return sword
  15.  
  16.     @classmethod
  17.     def load(self, sword_element):
  18.         name = sword_element.getElementsByTagName("Name")[0]
  19.         return Sword(name.firstChild.wholeText)

But we can do even better than this:
:

  1. import xml.dom.minidom as dom
  2.  
  3. def todom(sexpr):
  4.     if type(sexpr) is list or type(sexpr) is tuple:
  5.         node = dom.Element(sexpr[0])
  6.         for child in sexpr[1:]:
  7.             node.appendChild(todom(child))
  8.     else:
  9.         node = dom.Text()
  10.         node.replaceWholeText(str(sexpr))
  11.     return node

The above function converts s-expressions into XML, and we can use this to considerably shorten the code of our Sword class:
:

  1. class Sword:
  2.     def __init__(self, name):
  3.         self.name = name
  4.  
  5.     def dump(self):
  6.         return todom(("Sword" ("Name", self.name)))
  7.  
  8.     @classmethod
  9.     def load(self, sword_element):
  10.         name = sword_element.getElementsByTagName("Name")[0]
  11.         return Sword(name.firstChild.wholeText)

There's probably quite a few XML serialisation libraries worth looking into as well... Though I'd personally look into YAML as an alternative to storing human-editable data to file.

MR.T Jun 9th, 2007 11:16 PM

Ok I see, thank you very much for the informative post:)


All times are GMT -5. The time now is 8:10 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC