[Checkins] SVN: zope2book/trunk/XML.stx XML Document??? Haven't seen that one in ages: be gone!
Hanno Schlichting
plone at hannosch.info
Mon Feb 16 17:57:54 EST 2009
Log message for revision 96612:
XML Document??? Haven't seen that one in ages: be gone!
Changed:
D zope2book/trunk/XML.stx
-=-
Deleted: zope2book/trunk/XML.stx
===================================================================
--- zope2book/trunk/XML.stx 2009-02-16 22:55:10 UTC (rev 96611)
+++ zope2book/trunk/XML.stx 2009-02-16 22:57:53 UTC (rev 96612)
@@ -1,1108 +0,0 @@
-Chapter 9: Using XML
-
- XML is becoming an important data interchange format. More and more
- services are being made available over the web using XML. XML is a
- format to describe structured documents and data. Since XML is a
- widely supported standard it provides a good medium for exchanging
- structured information between systems.
-
- Zope supports XML on many fronts. You can generate XML from Zope
- objects thus allowing foreign systems to understand you, and you can
- import XML into Zope in order to decipher and manage it. Zope also
- supports exporting Zope objects in an XML format, and it supports
- several XML-based Internet protocols including WebDAV and XML-RPC.
-
- Managing XML with XML Document
-
- You can use XML in Zope with XML Documents. XML Documents hold XML
- content that you can upload, download, and edit with the Zope
- management interface. You can also script XML Documents using the
- Internet standard Document Object Model (DOM) API. Zope is much
- more than an XML repository, since once your XML data is in Zope
- it can take advantage of all Zope's services such as persistence,
- security, cataloging, presentation, and more.
-
- Using XML Document
-
- To create an XML Document, choose *XML Document* from the
- product add list. Then click the *Add* button. You will be taken
- to an XML Document add form as shown in Figure 9-1.
-
- "XML Document add form":img:Figures/uc.png
-
- The *Id* and *Title* fields allow you to specify a standard Zope
- id and title for your XML Document. The *File* field lets you
- upload an XML file from your local computer. You can create the
- XML Document by either clicking the *Add* or the *Add and Edit*
- buttons.
-
- After you create an XML Document you can change its XML in two
- different ways. You can edit the XML through the web as text,
- and you can manipulate the document's XML elements as Zope
- sub-objects.
-
- Editing XML
-
- To edit a document's XML go to the *Edit* view. Here you can
- change your document's XML as shown in Figure 9-2.
-
- "Editing XML":img:Figures/uc.png
-
- You can type XML right in your browser. If you make an error
- and enter invalid XML, Zope will complain. After you have
- changed your document's XML click the *Change* button. Zope
- does not currently validate XML against a DTD or schema. Later
- versions of XML Document will probably allow validation.
-
- You can also change the XML of a document by uploading an XML
- file from your local computer. Go to the *Upload* view. Here
- you can select an XML file to upload. When you upload an XML
- file, you completely replace the contents of your XML
- Document. As always, you can undo this action if you make a
- mistake.
-
- Accessing Elements
-
- An interesting feature of XML Documents is that they represent
- their contents as Zope objects. In other words, you can access
- your document's XML not just as text, but as objects. Go to
- the *Contents* view of an XML Document to see it's contents as
- objects as shown in Figure 9-3.
-
- "XML Document *Contents* view":img:Figures/uc.png
-
- As you can see your document's XML elements are represented as
- Zope objects. You can cut, copy, paste, and delete them like
- normal Zope objects. This allows you to move XML elements
- around in your document. Note that you cannot move elements
- out of your document, nor can you move other types of objects
- like Folders into your document.
-
- You can also rearrange the order of your document's XML
- elements using the *Shift Up* and *Shift Down* buttons. Select
- an element and click the *Shift Up* button. The element moves
- up in the list of elements. Notice the element's id may change
- as a result of shifting it. XML elements are named according
- to their element name and their position. For example the
- second *para* element has an id of *para-2*. If you move this
- element before the first *para* element, its id will change
- to *para-1*.
-
- If you click on an element you will then be taken to the
- element's management screen as shown in Figure 9-4.
-
- "XML element management":img:Figures/uc.png
-
- As you can see, elements also can have sub-elements. So not
- only can you move top-level elements around, but you can
- elements around into other elements. For example, create an
- XML Document named *family.xml* with these contents::
-
- <family>
-
- <mother>
- <eyes color="brown"/>
- <ears size="small"/>
- </mother>
-
- <father>
- <eyes color="blue"/>
- <ears size="large"/>
- </father>
-
- <child/>
-
- <child/>
-
- </family>
-
- Now go to the *Contents* view of the document and navigate to
- the *mother-1* element. Select the *eyes-1* element and click
- *Copy*. Now go back up to the *family-1* element and navigate
- down into the *child-1* element. Click the *Paste* button. Now
- return to the document and go to the *Edit* view. You should
- see that the *child* element now has the same *eyes*
- sub-element as the *mother* element.
-
- You may have noticed that elements have ids corresponding to
- their tag names. For example, the *family* tag has an id of
- *family-1*. The number following the tag name indicates the
- number of the element. For example, notice that the first
- *child* element has an id of *child-1* while the second
- *child* tag has an id of *child-2*. Since you can have more
- than one element with the same tag name, it is necessary to
- use a number in the element id to ensure that each element has
- a unique id.
-
- Since XML elements are Zope objects with unique ids, you can
- treat them just like other Zope objects. For example, you can
- visit an XML element at its URL. You can call acquired methods
- on elements. You can catalog elements. You can walk up to
- elements and manage them. In the course of this chapter we'll
- show you how to do all these things with XML elements.
-
- Editing Elements
-
- You may have noticed that elements have several views in
- addition to the *Contents* view. You can edit the XML of an
- element directly by going to the *Edit* view. You can also
- replace an element by going to the *Upload* view and uploading
- an XML file.
-
- For example, go to the *Edit* view of the *child* node from
- the last example. You should see::
-
- <eyes color=brown/>
-
- This is shows you the element you pasted in from another
- element. Change the contents of the element to::
-
- <eyes color="brown"/>
- <ears size="medium"/>
-
- Click the *Change* button. Now you can go to the *Contents*
- view and see that your element now contains an new *ears-1*
- element. You can also verify you changes by returning to the
- *Edit* view of the XML Document. You should see that your
- changes to the element are reflected in the contents of the
- document.
-
- Viewing the DOM
-
- XML Documents and elements give you a way to get a graphical
- view of their contents. Go to the *DOM Hierarchy* view to see
- tree view of your document's XML as shown in Figure 9-5.
-
- "XML Document DOM Hierarchy":img:Figures/uc.png
-
- You can expand and collapse the tree by clicking on the plus
- and minus signs next to individual nodes. You can also
- completely expand or collapse the tree with the links at the
- top of the screen. To manage an element click on it. In
- addition to viewing the DOM tree from an XML Document, you can
- view a portion of he DOM tree by navigating to an element and
- then going to the *DOM Hierarchy* view on that element. This
- will show you the branch of the DOM tree from your
- element. The *DOM Hierarchy* view is mostly useful as a way to
- examine the structure of your XML and quickly navigate to
- different elements.
-
- The DOM API
-
- The DOM API is the standard Internet API for querying and
- controlling XML documents. Zope supports "DOM Level
- 2":http://www.w3.org/TR/DOM-Level-2 including the traversal
- extensions as defined by the "World Wide Web
- Consortium":http://www.w3.org. Consortium. The DOM is a fairly
- complex API that defines how you can access and manipulate an
- XML document. A complete discussion of the DOM is beyond the
- scope of this book. See Appendix A for a summary of the DOM API
- as supported by XML Document. You can use the DOM API from
- DTML, Python, and Perl to query and change XML Documents.
-
- Displaying XML with DTML
-
- Until XSLT Methods are available, DTML Methods are your best
- choice for displaying XML Documents. You can use DTML to display
- XML Documents exactly the same way you use DTML to display other
- Zope objects. For example suppose you want an XML Document that
- describes a number of invoices. Create an XML with an id of
- *invoices.xml* with these contents::
-
- <invoices>
- <invoice>
- <number>127</number>
- <company>Acme Feedbags</company>
- <amount>34.00</amount>
- <status>Overdue</status>
- </invoice>
- <invoice>
- <number>128</number>
- <company>Vet Tech</company>
- <amount>55.00</amount>
- <status>Normal</status>
- </invoice>
- </invoices>
-
- To display the invoices in HTML create a DTML Method named
- *viewInvoices* with this DTML code::
-
- <dtml-var standard_html_header>
-
- <h2>Invoices</h2>
-
- <table>
- <tr>
- <th>Invoice Number</th>
- <th>Company</th>
- <th>Amount</th>
- <th>Status</th>
- </tr>
- <dtml-in expr="documentElement.getElementsByTagName('invoice')">
- <tr>
- <td>
- <dtml-in expr="getElementsByTagName('number')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in>
- </dtml-in>
- </td>
- <td>
- <dtml-in expr="getElementsByTagName('company')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in>
- </dtml-in>
- </td>
- <td>
- <dtml-in expr="getElementsByTagName('amount')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in>
- </dtml-in>
- </td>
- <td>
- <dtml-in expr="getElementsByTagName('status')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in>
- </dtml-in>
- </td>
- </tr>
- </dtml-in>
- </table>
-
- <dtml-var standard_html_footer>
-
- You can use this method to display your XML data by going to the
- URL *http://localhost:8080/invoices.xml/viewInvoices*. The
- resulting web page is shown in Figure 9-6.
-
- "Displaying an XML Document with DTML":img:Figures/uc.png
-
- This DTML Method is rather complex since it requires so many DOM
- method calls. It loops over all the *invoice* elements using the
- *getElementsByTagName* method. For each *invoice* element it
- finds the contained *number*, *company*, *amount*, and *status*
- elements and displays their child text nodes.
-
- You could also choose to display each invoice on a separate web
- page. Create a DTML Method named *viewInvoice* with these
- contents::
-
- <dtml-var standard_html_header>
-
- <h2>Invoice</h2>
-
- <p>
- <dtml-if previousSibling>
- <dtml-with previousSibling>
- <a href="&dtml-absolute_url;/viewInvoice">Previous invoice</a>
- </dtml-with>
- </dtml-if>
-
- <dtml-if nextSibling>
- <dtml-with nextSibling>
- <a href="&dtml-absolute_url;/viewInvoice">Next invoice</a>
- </dtml-with>
- </dtml-if>
- </p>
-
- <table>
- <tr>
- <th>Number</th>
- <td><dtml-in expr="getElementsByTagName('number')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in></dtml-in></td>
- </tr>
- <tr>
- <th>Company</th>
- <td><dtml-in expr="getElementsByTagName('company')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in></dtml-in></td>
- </tr>
- <tr>
- <th>Amount</th>
- <td><dtml-in expr="getElementsByTagName('amount')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in></dtml-in></td>
- </tr>
- <tr>
- <th>Status</th>
- <td><dtml-in expr="getElementsByTagName('status')">
- <dtml-in childNodes><dtml-var nodeValue></dtml-in></dtml-in></td>
- </tr>
- </table>
-
- <dtml-var standard_html_footer>
-
- Call this method on the first invoice node by going to this URL
- *http://localhost:8080/invoices/invoices-1/invoice-1/viewInvoice*. You
- should see a web page as shown in Figure 9-7.
-
- "Displaying an XML Element with DTML":img:Figures/uc.png
-
- An interesting thing to notice is how this display method
- creates links between invoice elements. It checks the
- *previousSibling* and *nextSibling* DOM attributes. If they are
- present, it uses the *absolute_url* method to create a link to
- the elements.
-
- It's a fairly common pattern when working with a large XML
- Document to create templates for elements rather than for the
- complete document. Each element template can include
- navigational links to allows you to move between
- elements.
-
- Using XML with Python and Perl
-
- You can use Python and Perl to query and change XML
- Documents. You can call DOM methods and access DOM attributes on
- individual elements of an XML Document. For example, create an
- XML Document with an id of *addressbook.xml* and these contents::
-
- <addressbook>
- <item>
- <name>Bob</name>
- <address>2118 Wildhare st.</address>
- </item>
- <item>
- <name>Randolf</name>
- <address>13 E. Roundway</address>
- </item>
- </addressbook>
-
- You can query this XML Document in Python in a number of
- ways. Here is a Script that does some testing
- on the XML Document::
-
- ## Script (Python) "query"
- ##
- import string
- # get the XML Document, must use getattr since
- # the id is not a legal Python identifier
- doc=getattr(context, 'addressbook.xml')
-
- # get the document element
- book=doc.documentElement
-
- # count items, assuming all children are items
- print "Number of items", len(book.childNodes)
-
- # get names of items
- names=[]
- for item in book.childNodes:
- # assumes first child is name
- name=item.firstChild
- # assumes name has one child which is a text node
- names.append(name.firstChild.nodeValue)
- print "Names ", string.join(names, ",")
- return printed
-
- Querying an XML Document with DOM may be a bit tedious, but it's
- relatively straight forward. You can also write scripts in
- Python and Perl that can query elements of XML Documents. For
- example, here's a Script that expects to be
- called on an *item* element. It returns the content of the
- *name* sub-element::
-
- ## Script (Python) "itemName"
- ##
- # context is assumed to be an item element
- return context.firstChild.firstChild.nodeValue
-
- You can call this method on the first item in the
- *addressbook.xml* document by going to this URL
- *http://localhost:8080/addressbook.xml/addressbook-1/item-1/itemName*.
- You could also call this method on an element from another DTML
- method or a Script. For example, in an earlier section you saw how
- you can call a DTML Method directly on an element to display
- it. The DTML Method could call Scripts on the element in
- order to query the element in ways that would be difficult to do
- from DTML.
-
- In addition to querying, Scripts excel at
- modifying XML. You can call DOM methods to edit elements and
- move them around. For example, here's a Script to add a
- new *item* element to the *addressbook.xml* XML Document::
-
- ## Script (Python) "addItem"
- ##parameters=name, address
- ##bind context=doc
- ##
- # call this script on an XML Document
-
- # create item element and its sub-elements
- item=doc.createElement('item')
-
- elname=doc.createElement('name')
- elname.appendChild(doc.createTextNode(name))
- item.appendChild(elname)
-
- eladdr=doc.createElement('address')
- eladdr.appendChild(doc.createTextNode(address))
- item.appendChild(eladdr)
-
- # add complete item to addressbook
- book=doc.documentElement
- book.appendChild(item)
-
- This script creates a new *item* element along with its
- sub-elements, *name* and *address*. It then inserts the *item*
- element into the *addressbook* element.
-
- Here's another example using two Scripts to
- rearrange the *item* elements in alphabetical order::
-
- ## Script (Python) "compareItems"
- ##parameters=x, y
- ##
- """
- Compares two item elements alphabetically. Returns -1,
- 0, or 1 to indicate less, equal, and greater.
-
- Used by the sortItems script to sort a list of address
- elements.
- """
- return cmp(x.firstChild.firstChild.nodeValue,
- y.firstChild.firstChild.nodeValue)
-
-
- ## Script (Python) "sortItems"
- ##
- """
- Sorts the address elements of an XML Document
-
- Call this method on an XML Document
- """
- # remove item elements
- items=[]
- book=context.documentElement
- for item in book.childNodes:
- book.removeChild(item)
- items.append(child)
-
- # sort item elements
- items.sort(context.compareItems)
-
- # insert them back
- for item in items:
- book.appendChild(item)
-
- This script works by removing the *item* elements one by one
- from the *addressbook* element. It builds a Python list of
- items, and when it finishes removing and sorting the *items*,
- it adds them back to the *addressbook* in sorted order.
-
- Rather than adding items to your address book and then sorting
- the entire thing, it would be more efficient to add items in
- the correct order in the first place. That way your address
- book is always in order. Here's a revision of the *addItem*
- script that adds items in the correct place so that the
- address book stays sorted::
-
- ## Script (Python) "addItem"
- ##parameters=name, address
- ##bind context=doc
- # call this method on an XML Document
-
- # create item element and its sub-elements
- item=doc.createElementNS('', 'item')
-
- elname=doc.createElementNS('', 'name')
- elname.appendChild(doc.createTextNode(name))
- item.appendChild(elname)
-
- eladdr=doc.createElementNS('', 'address')
- eladdr.appendChild(doc.createTextNode(address))
- item.appendChild(eladdr)
-
- # figure out where to insert item using bisect algorithm
- book=doc.documentElement
- items=book.childNodes
- lo=0
- hi=len(items)
- while lo < hi:
- mid = (lo + hi) / 2
- if name < items[mid].firstChild.firstChild.nodeValue:
- hi = mid
- else:
- lo = mid + 1
-
- # insert item
- if lo == len(childNodes):
- book.appendChild(item)
- else:
- before=items[lo]
- book.insertBefore(item, before)
-
- Don't worry if you don't understand how this script sorts
- items. The important point is to see that you can use Python's
- expressive logic to work on XML data.
-
- In addition to using Python and Perl to manipulate XML using
- DOM, you can use Python and Perl to use Zope services with XML
- data. For example, instead of maintaining your address book as
- an XML Document, you could maintain it with Zope objects. You
- could then restrict your use of XML to importing and exporting
- data from your Zope address book. While this design may seem
- more difficult, it often proves better in practice. XML elements
- can be treated as Zope objects but often these is a mismatch
- between XML elements and application objects. For example in the
- address book example, the *name* element is a sub-object of an
- *item* element. However in your application you may decide that
- it's better to have *person* objects that contain one or more
- *address* objects. You may not be able to change your XML format
- to fit your application design since other services may expect
- XML in this format. Later in the chapter you'll see an example
- of how to import and export XML from an application without
- having to store your data as XML. For serious applications this
- approach is often better.
-
- Cataloging XML Documents and Elements
-
- One especially interesting service that Zope can provide to XML
- data is searching. Using ZCatalog you can catalog XML Documents
- and their elements. See Chapter 9 for more information on
- ZCatalog. With ZCatalog you can perform full-text searching of
- XML elements. This gives you a lot of control over XML data.
-
- For example suppose you have an archive of
- "docbook":http://www.docbook.org/ XML articles. They consist of
- an *article* with a number of *section* elements. Each *section*
- element contains a *title* element, some *para* elements, and
- optionally additional *section* elements. Here's an example
- document:
-
- <article>
- <title>The History of Haircutting</title>
- <section>
- <title>Prehistory of Barbering</title>
- <para>Before scissors and razors were invented people cut
- hair with sharp rocks. If rocks were not available a
- barbers own teeth were his next best option.</para>
- </section>
- <section>
- <title>Modern Haircutting</title>
- <para>In these enlightened times hair is most often cut
- with whirling-bladed devices attached to vacuum
- cleaners.</para>
- <section>
- <title>Modern Hairstyles</title>
- <para>Modern hairstyles favor form over function, and
- present a true challenge to today's hairstylists.</para>
- </section>
- </section>
- </article>
-
- To catalog articles like this you need to create scripts that
- return the text you'd like to index. For this example, let's
- index *section* elements. This will allow you to search for text
- and find all the sections in all the articles that include the
- search terms. Probably you'll want two scripts: one to return
- the full text of a *section* element, and another to return the
- text of the section's title. You'll use the full-text script to
- index *section* objects, and the title script to get meta-data
- on the section. This will allow you to return the titles of all
- the sections that match a given query.
-
- Create a Script named *section_title* to
- return the text of a section's title::
-
- ## Script (Python) "section_title"
- """
- Text of a section element's title element.
- """
- for child in context.childNodes:
- if child.tagName == 'title':
- return child.firstChild.nodeValue
-
- Now create a Script named *section_fulltext* to
- return the full text of the *section* element::
-
- ## Script (Python) "section_fulltext"
- """
- Full text of a section element. Does not include text of
- contained section elements.
- """
- text=""
- for child in context.childNodes:
- if child.tagName in ('para', 'title'):
- text = text + " " + child.firstChild.nodeValue
- return text
-
- This script returns text of all *para* and *title* sub-elements
- of a *section* element.
-
- Now that you've created both the necessary scripts, it's time to
- create a ZCatalog and catalog your articles. Create a ZCatalog
- at the same level or above the location of you articles. Name
- the catalog *articleCatalog*. Now go to the *Indexes* view and
- delete all the existing indexes. Next create a new TextIndex
- named *section_fulltext*. This tells the catalog to call the
- *section_fulltext* script on all cataloged objects and to treat
- the result as full text. Now go to the *MetaData Table* view and
- delete all the existing meta-data columns. Create a new column
- named *section_title*. This tells the catalog to call the
- *section_title* script on all cataloged objects and to store the
- result as meta-data which will be available on result
- objects. Now that you've set the indexes and meta-data it's time
- to find and catalog all the *section* elements in your
- articles. Go to the *Find Items to ZCatalog* view. In the *expr*
- field enter *tagName=='section'* and click the *Find*
- button. This tells the catalog to search for objects whose
- *tagName* attribute is equal to the string *section*. You should
- be taken to the *Cataloged Objects* view and you should see that
- the catalog now contains a list of *section* elements.
-
- Now you can create a search and results form for the
- catalog. Create a DTML Method inside the catalog named *Search*
- with these contents::
-
- <dtml-var standard_html_header>
-
- <h2>Search Articles</h2>
-
- <form action="Results">
- Search terms <input type="text" name="section_fulltext">
- <input type="submit" value="Search">
- </form>
-
- <dtml-var standard_html_footer>
-
- Next you need to create a results form that will be called by
- the search form. Create another DTML Method inside the catalog
- named *Results* with these contents::
-
- <dtml-var standard_html_header>
-
- <h2>Found Articles</h2>
-
- <dtml-in searchResults>
- <a href="<dtml-var expr="getpath(data_record_id_)" url_quote>"><dtml-var section_title></a><br>
- </dtml-in>
-
- <dtml-var standard_html_footer>
-
- Congratulations, you've implemented a fine-grained full text
- XML search. View the *Search* method to test it out. Sure enough
- it will find matching *section* element.
-
- Unfortunately you don't have any way to display the matching
- *section* elements yet. To complete the example you should
- create a DTML Method to display a *section* element. Better yet
- you might want to create a method to display an article that
- includes internal anchors so that you could call it with a
- section identifier to display the article beginning with a given
- section. While XSLT is probably the right choice for such a
- method, it could be done in DTML. The implementation of the
- article and section display methods is left as an exercise for
- the ambitious reader.
-
- Controlling XML Parsing
-
- XML Document gives you a fair amount of low-level control over
- how it parses XML. To tell XML Documents how to parse XML you
- can create *XML Parsing Option* objects. Right now you can
- control how Zope handles white space and storage of elements. In
- the future you'll be able to control XML validation. XML Parsing
- Options are only needed by advanced users. You can safely ignore
- them until you find that you need more control over how Zope
- parses your XML.
-
- Create a XML Parsing Option named *myOption*. You should see an
- add screen as shown in Figure 9-8.
-
- "XML Parsing Option add screen.":Figures/uc.png
-
- The *ignoreWSTextNodes* option tells Zope to ignore white space
- between elements when parsing XML. You should ignore white space
- unless you have a specific reason not to. The second option
- lets you tell Zope which XML elements you want to store
- separately in the Zope database. In the *PersistElementTags*
- field you should enter the namespace and tag name for each type
- of element that you want to store separately. The reason to
- store elements separately is control performance and memory
- use. Increasing the number of persistent elements, increases the
- performance hit, but lowers the memory usage. The more
- persistent element you have the more the database needs to work
- to retrieve them. However by breaking your XML data into a
- number of persistent elements you lessen the amount of data that
- needs to be in memory because Zope can move unneeded persistent
- elements out of memory. These issues only really come into play
- when you are using very large XML Documents. In these cases
- you'll need to experiment with different settings to find the
- right trade off between speed and memory use. For now leave the
- *PersistElementTags* field blank and click the *Add* button.
-
- Now whenever you create an XML Document you'll be able to
- specify your XML Parsing options. For example create an new XML
- Document. You should now see the XML Parsing Option you just
- created as an option available to you on the XML Document add
- form.
-
- Generating XML
-
- All Zope objects can create XML. In fact, there is no need to use
- XML Document to create XML. It's fairly easy to create XML with
- DTML. For example suppose you have a folder that contains a number
- of documents describing food. You could represent this data with
- XML like so::
-
- <documents>
- <document>
- <title>Quiche</title>
- </document>
- <document>
- <title>Spaghetti</title>
- </document>
- <document>
- <title>Turnips</title>
- </document>
- </documents>
-
- This XML DTD may not be that complex but it's easy to
- generate. Create a DTML Method named "documents.xml" with the
- following contents::
-
- <documents>
- <dtml-in expr="objectValues('DTML Document')">
- <document>
- <title><dtml-var title></title>
- </document>
- </dtml-in>
- </documents>
-
- As you can see, DTML is equally adapt at creating XML as it is at
- creating HTML. Simply embed DTML tags among XML tags and you're
- set. The only tricky thing that you may wish to do is to set the
- content-type of the response to *text/xml* which can be done with
- this DTML code::
-
- <dtml-call expr="RESPONSE.setHeader('content-type', 'text/xml')">
-
- The whole point of generating XML is producing data in a format
- that can be understood by other systems. Therefore you will
- probably want to create XML in an existing format understood by
- the systems you want to communicate with. Suppose you have a
- collection of news items that you want to share with a news
- service using the RSS (Rich Site Summary) XML format. RSS is a
- format developed by Netscape for its *my.netscape.com* site, which
- has since gained popularity among other news sites. Here's what an
- example RSS file looks like::
-
- <?xml version="1.0"?>
-
- <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
- "http://my.netscape.com/publish/formats/rss-0.91.dtd">
-
- <rss version="0.91">
- <channel>
- <title>Zope.org</title>
- <link>http://www.zope.org/</link>
- <description>News from Zope.org</description>
- <language>en-us</language>
-
- <image>
- <title>Zope.org</title>
- <url>http://www.zope.org/Images/zbutton</url>
- <link>http://www.zope.org/</link>
- <width>78</width>
- <height>77</height>
- <description>Zope.org</description>
- </image>
-
- <item>
- <title>Zope hotfix: ZPublisher security update</title>
- <link>http://www.zope.org/Products/Zope/Hotfix_2000-10-02/security_alert</link>
- </item>
-
- <item>
- <title>First development release of HiperDom</title>
- <link>http://www.zope.org/Members/lalo/HiperDom/Announce_0.1</link>
- </item>
-
- <item>
- <title>Decode barcodes using DTML</title>
- <link>http://www.zope.org/Members/stevea/barcode_to_amazon/barcode_to_amazon_news</link>
- </item>
- </channel>
- </rss>
-
- This is an actual RSS file create using DTML on the
- *www.zope.org*
- website. It is built from a catalog query of news items. The main
- features of an RSS file are the *channel* and *item*
- elements. The *channel* element contains information about the
- news source and also contains the *items* elements. Each *item*
- element contains information about new items. In this example the
- *item* elements come from the results of a catalog search. Here's
- how this RSS is built::
-
- <dtml-call "RESPONSE.setHeader('content-type', 'text/xml')"><?xml version="1.0"?>
-
- <!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN"
- "http://my.netscape.com/publish/formats/rss-0.91.dtd">
-
- <rss version="0.91">
- <channel>
-
- <title>Zope.org</title>
- <link>http://www.zope.org/</link>
- <description>News from Zope.org</description>
- <language>en-us</language>
-
- <image>
- <title>Zope.org</title>
- <url>http://www.zope.org/Images/zbutton</url>
- <link>http://www.zope.org/</link>
- <width>78</width>
- <height>77</height>
- <description>Zope.org</description>
- </image>
-
- <dtml-in expr="searchResults(
- meta_type='News Item',
- sort_on='date',
- sort_order='reverse')" size=3">
- <item>
- <title><dtml-var title></title>
- <link><dtml-var BASE0>/<dtml-var url></link>
- </item>
- </dtml-in>
-
- </channel>
- </rss>
-
- This method is mostly static XML, only the *item* elements are
- dynamically generated. You could support RSS more flexibly by
- using objects and properties to keep track of channel
- information. You could also gather information for *item* elements
- in many way besides searching a catalog. For example you could
- directly iterate over Zope objects, or you could use Python
- Scripts to retrieve information from the network or the
- filesystem.
-
- If using DTML to create XML is to easy for your taste you can use
- XML Document instead to programmatically build an XML using
- DOM. There may be cases where this is necessary, but most often
- DTML will work fine. It's important to remember that XML is a
- format for communication. Use it pragmatically to enable your web
- applications to communicate. Often this is little need to store
- XML data internally in your application. Usually you can generate
- XML from Zope objects when you need to send it and parse XML into
- Zope objects when you need to read it.
-
- Processing XML
-
- A common use of XML is to communicate information. For the
- receiver to understand the communication, it needs to decode the
- XML message. Zope already does understands some kinds of XML
- messages such as XML-RPC and WebDAV. As you create web
- applications that communicate with other systems you may want to
- have the ability to receive XML messages. You can receive XML a
- number of ways, you can read XML file from the file system or over
- the network, or you can define methods that take XML arguments
- which can be called by remote systems.
-
- Once you have received an XML message you must process the XML to
- find out what it means and how to act on it. You have two basic
- choices within Zope for processing XML. You can create an XML
- Document and use the DOM API to examine the XML. Alternately you
- can manually parse the XML using Python or Perl's XML parsing
- facilities. Using XML Document and DOM requires less programming
- for simple cases, but can be unwieldy and inefficient, especially
- for large amounts of XML.
-
- You've already seen how to process XML using XML Document and DOM,
- so now let's take a quick look at how you might parse XML manually
- using Python. Suppose you want to connect you web application to a
- "Jabber":http://www.jabber.com/ chat server. You might want to
- allow users to message you and receive dynamic responses based on
- the status of your web application. For example suppose you want
- to allow users to check the status of their items using instant
- messaging. Your application should respond to XML instant messages
- like this::
-
- <message to="webapp at example.com" from="user at host.com">
- <body>status</body>
- </message>
-
- You could scan the body of the message for commands, call method
- and return responses like this::
-
- <message to="user at host.com" from="webapp at example.com">
- <body>All is well as of 3:12pm</body>
- </message>
-
- Here's a sketch of how you could implement this XML messaging
- facility in your web application using a Python External Method::
-
- # uses Python 2's standard xml processing package
- # see http://www.python.org/doc/current/lib/module-xml.sax.html
- # for information about Python's SAX (Simple API for XML) support
-
- from xml.sax import parseString
- from xml.sax.saxlib import DocumentHandler
-
- class MessageHandler(DocumentHandler):
- """
- SAX message handler class
-
- Extracts a message's to, from, and body
- """
-
- inbody=0
- body=""
-
- def startElement(self, name, attrs):
- if name=="message":
- self.to=attrs['to']
- self.from=attrs['from']
- elif name=="body":
- self.inbody=1
-
- def endElement(self, name):
- if name=="body":
- self.inbody=0
-
- def characters(self, data, start, length):
- if self.inbody:
- self.body=self.body + data[start:start+length]
-
-
- def receiveMessage(self, message):
- """
- Called by a Jabber server
- """
- handler=MessageHandler()
- parseString(message, handler)
-
- # call a method that returns a response string
- # given a message body string
- response_body=self.getResponse(handler.body)
-
- # create a response XML message
- response_message="""
- <message to="%s" from="%s">
- <body>%s</body>
- </message>""" % (handler.from, handler.to, reponse_body)
-
- # return it to the server
- return response_message
-
- This External Method uses Python's SAX (Simple API for
- XML) package to parse the XML message. The *MessageHandler* class
- receives callbacks as Python parses the message. The handler saves
- information its interested in. The method uses the handler
- class by creating an instance of it, and passing it to the
- *parseString* function. It then figures out a response message by
- calling *getResponse* with the message body. This method
- presumably scans the body for commands, queries the web
- applications state and returns some response. The *receiveMessage*
- method then creates a XML message using response and the sender
- information and returns it.
-
- The remote server would use this method by calling the
- *receiveMessage* method using the standard HTTP POST
- command. Voila, you've implemented a custom XML chat server that
- runs over HTTP.
-
- DOM API for All Zope Objects
-
- In addition to the Zope API, Zope objects support a subset of the
- DOM (Document Object Model) API. The "DOM":http://www.w3.org/DOM/ is
- an Internet standard for querying and scripting documents.
-
- DOM provides an interfaces to hierarchical data. DOM is designed to
- treat XML and HTML documents as collections of nodes. In the case of
- Zope's DOM support, you can use the DOM to query the Zope object
- hierarchy as a collection of nodes.
-
- DOM is a well documented and well understood API. If you've worked with
- DOM before you may find it more familiar and comfortable than Zope's
- API.
-
- DOM Methods and Attributes
-
- Zope supports the read-only methods and attributes of the
- level-2 DOM API. The DOM API represents Zope objects as DOM
- elements and string properties as DOM attributes. There are also
- a few additional bindings. For example, DOM node names
- correspond to Zope object meta-types. Also DOM node IDs
- correspond to Zope object ids.
-
- So for example, this is how you could use the DOM API to return
- a list of your sub-object's:
-
- results=[]
- for child in self.childNodes:
- results.append(child.nodeName)
- return results
-
- This will return a list of object types like so::
-
- zope:DTMLMethod
- zope:DTMLDocument
- zope:Folder
-
- This shows you that the DOM API interprets sub-objects as child
- nodes. It also demonstrates that node names are qualified by a
- namespace with the prefix *zope*. The URI of this namespace is
- *http://namespaces.zope.org/NullNamespace*. So using the DOM API
- you can effectively treat your sub-objects like XML nodes.
-
- Here's how you can use the DOM to find out the title of your
- first sub-object::
-
- child=self.firstChild
- return child.getAttributeNS(
- 'http://namespaces.zope.org/NullNamespace',
- 'title')
-
- This returns the value of the first child's *title*
- attribute. Suppose your first child was a DTML Method with a
- title of *display*. This XML is how the DOM API would understand
- the method::
-
- <zope:DTMLMethod
- xmlns:zope="http://namespaces.zope.org/NullNamespace" title="display">
- </zope:DTMLMethod>
-
- Notice that the contents of the DTML Method are not available
- via the DOM API. Also notice that all spaces in the meta-type
- have been removed. This is because XML doesn't allow spaces in
- element names.
-
- Another useful DOM method is *getElementsByTagNameNS*. This method
- recursively descends the object hierarchy searching for elements
- with a given tag name. The tag name of a Zope object is its
- meta-type (which is also considered its node name). Here is a
- bit of Python that will return all DTML Documents contained by
- the current object and all its sub-objects::
-
- return self.getElementsByTagNameNS(
- 'http://namespaces.zope.org/NullNamespace',
- 'DTMLDocument')
-
- You can use the asterisk as a tag name to indicate that you want
- to match all tag names. You can find further documentation of
- the Zope object implementation of the DOM API in Appendix B.
-
- Zope API versus DOM API
-
- You may have noticed that the DOM API on regular Zope objects
- doesn't buy you a lot. It's kind of nifty to use DOM methods,
- but *childNodes* isn't really any better than *objectValues*. In
- fact, many DOM methods are less flexible than normal Zope API
- methods. Using the DOM API on Zope objects has two important
- virtues that make it worthwhile:
-
- 1. It is a standard, so it's familiar and documented.
-
- 2. It allows technologies built on the DOM API to be added to
- Zope.
-
- Right now the second virtue has yet to flower fully. Two
- technologies that will be added to Zope on top of the DOM API
- are XPath and XSLT. For now the familiarity of the DOM is the
- most important reason to use it. If you already know DOM, then
- you may find it more comfortable than the normal Zope API for
- querying Zope about sub-objects and properties.
-
-
-
-
-
-
-
-
More information about the Checkins
mailing list