| 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:
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' |