ProjectPythonSourceForge
Welcome
Introduction
Suitability
Future Plans
Setup
Downloading XMLObject
Installing XMLObject
XMLObjApp
The XMLObjApp Application
Classes
Special Attributes
XML Attributes
Child Tags
File Menu
Miscellaneous Operation Notes
Unicode and ASCII Strings
Manually Editing Your Parser
Outputting XML
XMLObject
XMLObject -- XML to Object Conversion
Stack -- Tracks the Document Hierarchy
Object Interface
Examples
All Docs on One Page

Stack -- Tracks the Document Hierarchy

Object Interface

Stack is a class used to track an element's relationship to the rest of the document. Stack is passed to element constructors during XML parsing.

It defines the following members:

UniqueID(Class, ID, Obj, Stack)

UniqueID is a class used to implement the UniqueID XML attribute type. UniqueID holds a string type identifier. Access this identifier by casting the UniqueID to a string with the str function.

Note

UniqueIDs are instantiated by the parser. You should not need to instantiate a UniqueID yourself.

Class is the string name of the class in which the UniqueID can be found.

ID is the unique string value assigned to the instance.

Obj is class instance which will hold the identifier, i.e. the object that ReferenceID will want to access, given the associated value of ID.

Stack is the parser Stack. UniqueIDs are actually stored in the Stack object. This allows the ReferenceID object to cross-reference them.

ReferenceID(ID, Stack)

ReferenceID is a class used to implement the ReferenceID XML attribute type. ReferenceID allows you to cross reference another object with a matching UniqueID attribute.

Note

ReferenceIDs are instantiated by the parser. You should not need to instantiate a ReferenceID yourself.

Note

It is legal to instantiate a ReferenceID with a given value before the corresponding object with a matching UniqueID has been instantiated. A cross-reference search is not actually executed until a look-up is made.

ID is the unique string value assigned to some other object's UniqueID instance.

Stack is the parser Stack. UniqueIDs are actually stored in the Stack object. This allows the ReferenceID object to cross-reference them.

Examples

UniqueIDs serve two purposes; they allow elements to cross-reference each other, and they provide a mechanism to find an element by ID – regardless of where the element is located in the document hierarchy.

Example 5.1. Copied from Example 3.3, “Family Tree XML”

ReferenceIDs are called like functions to refer to other elements.

<Family>
    <Member Name="Abe" DOB="3/31/42" />
    <Member Name="Betty" DOB="2/4/49" />
    <Member Name="Cathy" DOB="12/2/78" />
    <Member Name="Dan" Father="Abe" Mother="Betty" DOB="6/12/73" />
    <Member Name="Edith" Father="Abe" Mother="Betty" DOB="8/30/80" />
    <Member Name="Frank" DOB="11/4/70" />
    <Member Name="George" Father="Dan" Mother="Cathy" DOB="5/13/94" />
    <Member Name="Harold" Father="Dan" Mother="Cathy" DOB="7/1/97" />
    <Member Name="Irwin" Father="Frank" Mother="Edith" DOB="10/31/01" />
    <Member Name="Janet" Father="Frank" Mother="Edith" DOB="1/17/03" />
</Family>
import XMLObject

class Member:
    ChildSpec = XMLObject.ChildClass({}, "")
    def __init__(self, Name, DOB=None, Father=None, Mother=None, XMLStack=None):
        self.DOB = str(DOB)
        self.Name = XMLStack.UniqueID("Member", Name, self, XMLStack)
        self.Father = XMLStack.ReferenceID(Father, XMLStack)
        self.Mother = XMLStack.ReferenceID(Mother, XMLStack)

class Family:
    ChildSpec = XMLObject.ChildClass({"Member": XMLObject.StdChild(Member,
        "Member", "Dict", Key="Name")}, "(<Member>)*")
    def __init__(self, XMLStack):
        self.Member = {}
>>> import XMLObject, tree
>>> Tree = XMLObject.Parse("tree.xml", "Family", tree.Family)
>>> Tree.Member["Janet"].DOB
'1/17/03'
>>> Tree.Member["Janet"].Mother().DOB
'8/30/80'
>>> Tree.Member["Janet"].Mother().Father().DOB
'3/31/42'

Example 5.2. Multiple UniqueIDs

Although effective in the previous example, what happens when you can't guarantee that your UniqueIDs are truly unique?

Suppose you have multiple tag types (<A> and <B>) with unique identifiers. If these identifiers are unique across a single tag type (all <A> UniqueIDs are unique and all <B> UniqueIDs are unique) but it's possible that both tag types may have overlapping identifiers, then you can't simply call a ReferenceID and know you'll get the right tag returned. In such cases, you must specify which tag type a ReferenceID call should return.

To illustrate this, consider the following, painfully-contrived example:

<Elements>
    <Fruit Name="apple" Color="red" Size="medium" />
    <Fruit Name="orange" Color="orange" Size="medium" />
    <Fruit Name="grape" Color="purple" Size="small" />
    <Color Name="red" Fruit="apple" />
    <Color Name="orange" Fruit="orange" />
    <Color Name="purple" Fruit="grape" />
</Elements>
import XMLObject

class Fruit:
    ChildSpec = XMLObject.ChildClass({}, "")
    def __init__(self, Name, Color=None, Size=None, XMLStack=None):
        self.Name = XMLStack.UniqueID("Fruit", Name, self, XMLStack)
        self.Color = XMLStack.ReferenceID(Color, XMLStack)
        self.Size = Size

class Color:
    ChildSpec = XMLObject.ChildClass({}, "")
    def __init__(self, Name, Fruit=None, XMLStack=None):
        self.Name = XMLStack.UniqueID("Color", Name, self, XMLStack)
        self.Fruit = XMLStack.ReferenceID(Fruit, XMLStack)

class Elements:
    ChildSpec = XMLObject.ChildClass({"Fruit": XMLObject.StdChild(Fruit,
        "Fruit", "List"), "Color": XMLObject.StdChild(Color, "Color", "List")},
        "((<Fruit>) | (<Color>))*")
    def __init__(self, XMLStack):
        self.Fruit = []
        self.Color = []
>>> import XMLObject, elements
>>> Elements = XMLObject.Parse("elements.xml", "Elements", elements.Elements)
>>> str(Elements.Color[0].Name)
'red'
>>> str(Elements.Color[1].Name)
'orange'
>>> str(Elements.Color[1].Fruit)
'orange'
>>> Elements.Color[1].Fruit.__class__
<class XMLObject.ReferenceID at 0x009832D0>
>>> Elements.Color[1].Fruit("Fruit").__class__
<class elements.Fruit at 0x009830F0>
>>> Elements.Color[1].Fruit("Fruit").Size
'medium'

As you can see above, Color[1] is the Color "orange". This Color has been cross-referenced back to the Fruit "orange". As before, we can follow this cross-referencing by calling the ReferenceID instance as if it were a function, but this time we must pass a tag type. If we did not, then we couldn't be sure if the ReferenceID would return the Fruit "orange"... or the Color.

Example 5.3. Searching a Stack Instance

Finding a specific element in an XML document can be a pretty tiresome process of searches and sub-searches, but if that element is uniquely identified, then finding it is a snap with XMLObject.

Consider the following, order status data:

<Customers>
    <Customer Name="Joe Blow">
        <Order ID="Z98212" Status="shipped" />
    </Customer>
    <Customer Name="Gre7g Luterman">
        <Order ID="H67921" Status="shipped" />
        <Order ID="M26611" Status="lost" />
    </Customer>
</Customers>
import XMLObject

class Order:
    ChildSpec = XMLObject.ChildClass({}, "")
    def __init__(self, ID, Status=None, XMLStack=None):
        self._root = XMLStack[-1]
        self._parent = XMLStack[0]
        self.Status = Status
        self.ID = XMLStack.UniqueID("Order", ID, self, XMLStack)

class Customer:
    ChildSpec = XMLObject.ChildClass({"Order": XMLObject.StdChild(Order,
        "Order", "Dict", Key="ID")}, "(<Order>)*")
    def __init__(self, Name, XMLStack=None):
        self.Name = Name
        self.Order = {}

class Customers:
    ChildSpec = XMLObject.ChildClass({"Customer": XMLObject.StdChild(Customer,
        "Customer", "Dict", Key="Name")}, "(<Customer>)*")
    def __init__(self, XMLStack=None):
        self.Customer = {}

Suppose we wanted to find out what happened to order number "M26611". Without taking advantage of the UniqueID class, we would need to do a has_key on each Customer to first find out which customer had that order number, and then look at the order number itself to check the status. That isn't terribly difficult in this simple example because the Orders are only one level down from Customer. Just imagine the extra work if they were 10 levels down!

>>> import XMLObject, customers
>>> Customers = XMLObject.Parse("customers.xml", "Customers",
... customers.Customers)
>>> Matches = filter(lambda Customer: Customer.Order.has_key("M26611"),
... Customers.Customer.values())
>>> len(Matches)
1
>>> Matches[0].Name
'Gre7g Luterman'
>>> Matches[0].Order["M26611"].Status
'lost'

As I mentioned in the Introduction, I don't like using filter and lambda functions this way. The resulting code is nice and compact, but not particularly readible or maintainable. If your code looks like the above, you might want to rethink it.

If we, instead, instantiate our own Stack, then we can use it to look up UniqueIDs:

>>> import XMLObject, customers
>>> Stack = XMLObject.Stack()
>>> Customers = XMLObject.Parse("customers.xml", "Customers",
... customers.Customers, Stack)
>>> Stack("M26611").Status
'lost'
>>> Stack("M26611")._parent.Name
'Gre7g Luterman'