Simple Example for Newbies

Help
dzerugral
2012-06-22
2016-12-21
  • dzerugral

    dzerugral - 2012-06-22

    Hi, I think many newbies will agree with me that documentation for this Python module is a bit overly complicated. Especially compared to JAXB (which I was able to work with without any prior knowledge of XML or XSD).

    Here is a simple example I beg for someone to demonstrate:

    A XSD file with nested elements or a slight modification to one of the posted examples as in:

    import pyxb
    import po4
    import address
    import pyxb.binding.datatypes as xs
    import datetime
    po = po4.purchaseOrder(orderDate=xs.date(1999, 10, 20))
    po.shipTo = address.USAddress('Alice Smith', '123 Maple Street', 'Anytown', 'AK', 12341)
    po.billTo = address.USAddress('Robert Smith', '8 Oak Avenue', 'Anytown', 'AK', 12341)
    po.items = pyxb.BIND(pyxb.BIND('Lapis necklace', 1, 99.95, partNum='833-AA'),
                         pyxb.BIND('Plastic necklace', 4, 3.95, partNum='833-AB'))
    po.shipTo.country = po.billTo.country = po.shipTo.country
    lapis = po.items.item[0]
    lapis.shipDate = po.orderDate + datetime.timedelta(days=46)
    lapis.comment = 'Want this for the holidays!'
    po.items.item[1].shipDate = po.items.item[0].shipDate + datetime.timedelta(days=19)
    print po.toxml("utf-8")
    

    Now, if item is a child of items and items is a child of purchase order how might we append a new item to a new purchase order?

    Is it done as simply as:

    pur_ord = po4.PurchaseOrder()
    itm = po4.Item()
    pur_ord.append(itm)
    print pur_ord.toxml()
    

    This is the approach I have taken and it doesn't seem to be the right way? Maybe a tutorial explaining the differences between BIND and append? Is there any documentation/tutorial on using Append?

    Thanks

     
    • BarnacleWes

      BarnacleWes - 2016-12-21

      Thank you thank you thank you! This got me over my 2-day stumbling block. Much much simpler example of creating the document from the python class, exactly what I needed, especially the syntax for adding multiple elements.

       
      • BarnacleWes

        BarnacleWes - 2016-12-21

        Curious, I came across this and wrote a test program using my own apprently badly formed xsd, and had success while plonking around directly in the python interpreter. This morning when I turned it into an actual script, it didn't work, the append failed. The difference was...

        While messing around in the interpreter, I was gratuitously sprinkling 'toxml' around to see what was happening. My xsd creates something called a 'spotUpdate', which has an updateID and a list of spots. This sequence works:

        import pyxb
        import spotUpdate
        
        s1=spotUpdate.Spot_ct()
        s1.ID='2'
        s1.numAircraft=13
        s1.bandwidth=38331
        
        s2=spotUpdate.Spot_ct()
        s2.ID='7'
        s2.numAircraft=22
        s2.bandwidth=146484135
        
        rec=spotUpdate.SpotUpdate()
        rec.updateID='fc3'
        
        # @note:this is required, even though it looks useless
        rec.toxml("utf-8", element_name='SpotUpdate')
        
        rec.append(s1)
        rec.append(s2)
        
        print(rec.toxml("utf-8", element_name='SpotUpdate'))
        

        If you comment the toxml line after the @note, the program fails on the first append with:

        Traceback (most recent call last):
          File "/home/wes/spots.py", line 20, in <module>
            rec.append(s1)
          File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/basis.py", line 2548, in append
            raise pyxb.UnrecognizedContentError(self, self.__automatonConfiguration, value, location)
        pyxb.exceptions_.UnrecognizedContentError: Invalid content {http://schema.customer.com/AMP}Spot_ct (expect updateID)
        

        I'm curious to know what it is 'toxml' does to the object to make it possible to append spots after a call to it. Is it completing the binding for the list of spots somehow? I tried playing around with pyxb.BIND() there, but didn't make much headway.

         
  • Peter A. Bigot

    Peter A. Bigot - 2012-06-22

    What you're running into here is a result of a choice made by the schema designer, and discussed at http://pyxb.sourceforge.net/userref_usebind.html#creating-instances-of-anonymous-types

    At the XML document level, a purchaseOrder has an element items which has within it zero or more item elements.

    However, the only one of those that has a corresponding element defined within the namespace is purchaseOrder.  All the rest are instances of what might be considered "inner classes".  Consequently, there is no way to create instances of them outside the context of an instance of their containing class.

    This is why pyxb.BIND had to be created: it's a way of telling PyXB to "give me one of whatever is needed at this point".

    You run into trouble in your example specifically because when you do:

    itm = po4.Item()
    

    there is nothing in the module that could be used to create an individual item in isolation.

    Play with the following example; hopefully that and the text in the manual will help make this clear.  And when you design schemas, make sure that you don't use anonymous types.

    import po4
    import pyxb
    # Set this so you can call toxml() on instances while they're being
    # constructed, for debugging purposes
    pyxb.RequireValidWhenGenerating(False)
    # Create a new purchase order
    pur_ord = po4.purchaseOrder()
    print pur_ord.toxml()
    # pur_ord.items is None when created as above.  We need to give it an
    # instance.  This is one of the few cases in this schema where there
    # is an available named type at the top level.
    pur_ord.items = po4.Items()
    print pur_ord.toxml()
    # Since the element type of pur_ord.items isn't known, create one using
    # pyxb.BIND():
    pur_ord.items.append(pyxb.BIND())
    print pur_ord.toxml()
    # Now pull it off the array and do stuff to it
    itm = pur_ord.items.item[-1]
    itm.productName = 'cheap trinket'
    itm.quantity = 4
    itm.USPrice = 22.35
    print itm.toxml()
    # Now that we have something of the right type, we can create new ones:
    itm2 = type(itm)(productName='expensive trinket', quantity=1, USPrice=1425.95)
    # BUT: You can't print it, because this is an instance of the type in
    # isolation from an element which would give it a name:
    # print itm2.toxml() << THIS DOESN'T WORK
    # You can, though, attach it to an element:
    pur_ord.items.item.append(itm2)
    print pur_ord.toDOM().toprettyxml()
    

    You should get something like this:

    <?xml version="1.0" ?><ns1:purchaseOrder xmlns:ns1="URN:purchase-order"/>
    <?xml version="1.0" ?><ns1:purchaseOrder xmlns:ns1="URN:purchase-order"><ns1:items/></ns1:purchaseOrder>
    <?xml version="1.0" ?><ns1:purchaseOrder xmlns:ns1="URN:purchase-order"><ns1:items><ns1:item/></ns1:items></ns1:purchaseOrder>
    <?xml version="1.0" ?><ns1:item xmlns:ns1="URN:purchase-order"><ns1:quantity>4</ns1:quantity><ns1:USPrice>22.35</ns1:USPrice><ns1:productName>cheap trinket</ns1:productName></ns1:item>
    <?xml version="1.0" ?>
    <ns1:purchaseOrder xmlns:ns1="URN:purchase-order">
            <ns1:items>
                    <ns1:item>
                            <ns1:quantity>4</ns1:quantity>
                            <ns1:USPrice>22.35</ns1:USPrice>
                            <ns1:productName>cheap trinket</ns1:productName>
                    </ns1:item>
                    <ns1:item>
                            <ns1:quantity>1</ns1:quantity>
                            <ns1:USPrice>1425.95</ns1:USPrice>
                            <ns1:productName>expensive trinket</ns1:productName>
                    </ns1:item>
            </ns1:items>
    </ns1:purchaseOrder>
    
     
  • Peter A. Bigot

    Peter A. Bigot - 2012-06-22

    By the way, I'd be most interested in seeing what the corresponding program in Java using JAXB would look like with such a schema.  How do you create instances of classes when they don't have a name within the package?

     
  • dzerugral

    dzerugral - 2012-06-25

    when I used JAXB annotations (kinda like decorators) are used to signify xml nodes within the java class. JAXB would then automatically detect complex types based on the nesting of classes within classes. No need to work with XML or XSD whatsoever. But the code is a little uglier with the annotations. Also you were forced to put "set" in front of all getter/setters in order for JAXB to realize it is an attribute (typical java verbosity).

    I guess my overall question/suggestion was there should be a simple example that doesn't include complexities of binding. Just instantiate, append child elements, write to xml and read/build from xml.

     
  • Peter A. Bigot

    Peter A. Bigot - 2012-06-25

    I guess my overall question/suggestion was there should be a simple example that doesn't include complexities of binding. Just instantiate, append child elements, write to xml and read/build from xml.

    That's fair.  http://pyxb.sourceforge.net/userref_usebind.html#creating-instances-in-python-code was intended to be that, using the same international purchase-order example as is in the W3C XML Schema Part 0 Primer and that was used in the previous section to demonstrate building bindings.  Unfortunately that schema soon requires BIND to do more complex things.

     
  • dzerugral

    dzerugral - 2012-06-25

    I originally saw that example (I studied every single one). Still, it didn't use append() or any nesting for that matter.

    I think having the requested example on the first introduction page of pyxb would be a seller! Personally, I turned to pyxb for the same reason I turned to JAXB - to avoid lower level xml parsing. Starting things off with the simplicity of pyxb for everyday xml chores would advertise what I think is its most elegant features.

    The example should start from generating a nested xsd (using eclipse xml editor since it auto-populates an accompanying xml file) then move on to the command line tool for generating the equivalent .py module (in windows and unix). Finally, a simple instantiation of the root object, append method, toxml method, createDocumentFromXml method, and finish with a print. To me, that is the optimum simple and fairly useful example (since nesting is common).

    I know this is a lot of hand waiving, but one last thing I wanted to mention was after installing pyxb on windows I could not use pyxb as a command in cmd.exe. In fact, I needed to reference (use complete paths) the actual .py module for generating pyxbgen. I tried this on two computers and both needed this (which made the command several lines long!).

     
  • dzerugral

    dzerugral - 2013-02-10

    So, here is an answer to my own noob question. Please feel free to use this or a modified version for any documentation purposes (I would have liked this example when I first started!):

    With some loss of generality here is the quickest way to get PyXB up and running for basic use. I use Eclipse/Pydev because eclipse has excellent XML tools that will make generating schemas very very easy. Of course, the XML schema is necessary because PyXB will auto-generate python objects based on the schema you feed it. So, I choose Eclipse/Pydev but whatever your preference may be make sure you have a way of easily crafting/editing XML schemas!

    So, start by getting the XML tools for eclipse. You can now create an XML schema ("universe_schema.xsd") with a neat GUI tool. Setup the schema to have the following hierarchy: Note  is notation for 1 or more elements - as in an "array" with at least 1 element.  is notation for "required". Meaning in order to satisfy the schema you must set that element.

    Create a root node of complex type "Universe" (call it "Universe")
    give the root node type an element with multiplicity  of complex type "Galaxy" (call the element "galaxies")
    give the "Galaxy" type an element with multiplicity  of complex type "Planet" (call the element "planets")

    give the "Galaxy" and "Planet" type an attribute of type String (call it "name")
    give the "Planet" type an element with multiplicity  of type int (call it "population")

    You're done with the schema! Now CD to the current directory of the schema and use the following PyXB command (on a windows machine it is similar to:)

    C:\Python27\python.exe C:\Python27\Scripts\pyxbgen -u universe_schema.xsd  -m universe_schema

    You now have a module called universe_schema.py that contains a class for all the Types you defined and their relationship to one another! Reference this module in the following code:

    import universe_schema
    # the root node has a "_" in its name because we named it the same name as its type (so PyXB automatically
    # generates it with a unique name!
    universe = universe_schema.Universe_()
    # create planets and galaxies of the universe (nothing special here just instantiating like any ole' class)
    galaxy1 = universe_schema.Galaxy()
    planet1 = universe_schema.Planet()
    planet2 = universe_schema.Planet()
    # xml elements and attributes are set like python class properties!
    galaxy1.name = "Milky Way"
    planet1.name = "earth"
    planet1.population = 6
    planet2.name = "mars"
    planet2.population= 0
    # you can access the attributes/elements too of course.
    print galaxy1.name
    # now we simply add the planets and galaxies just like we append/extend to python lists!!
    galaxy1.planets.extend(planet1, planet2)
    universe.galaxies.append(galaxy1)
    # create a file for generating an xml document of our universe for
    myfile = open('universe.xml', "w")
    myfile.write(universe.toxml())
    myfile.close()
    # check your work by reading the xml file back into its objected form (that was auto-generated by PyXB)
    universe_file = universe_schema.CreateFromDocument(file('universe.xml').read())
    # print the XML to the console and see what you made! Also, go check out the xml file you wrote.
    print universe_file.toxml()
    
     
  • dzerugral

    dzerugral - 2013-02-10

    sorry the code got messed up when it was indented, here it is cleaned:

    import universe_schema
    # the root node has a "_" in its name because we named 
    #it the same name as its type (so PyXB automatically
    # generates it with a unique name!
    universe = universe_schema.Universe_()
    # create planets and galaxies of the universe (nothing 
    # special here just instantiating like any ole' class)
    galaxy1 = universe_schema.Galaxy()
    planet1 = universe_schema.Planet()
    planet2 = universe_schema.Planet()
    # xml elements and attributes are set like python class properties!
    galaxy1.name = "Milky Way"
    planet1.name = "earth"
    planet1.population = 6
    planet2.name = "mars"
    planet2.population= 0
    # you can access the attributes/elements too of course.
    print galaxy1.name
    # now we simply add the planets and galaxies just 
    # like we append/extend to python lists!!
    galaxy1.planets.extend(planet1, planet2)
    universe.galaxies.append(galaxy1)
    # create a file for generating an xml document of our universe
    myfile = open('universe.xml', "w")
    myfile.write(universe.toxml())
    myfile.close()
    # check your work by reading the xml file back into its
    # objected form (that was auto-generated by PyXB)
    universe_file = universe_schema.CreateFromDocument(file('universe.xml').read())
    # print the XML to the console and see what you made! Also, 
    # go check out the xml file you wrote.
    print universe_file.toxml()
    
     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

JavaScript is required for this form.





No, thanks