This chapter looks at how XML can be used as a data exchange format (e.g., in files, or over the wire) between JDO-based applications. 13.4.1 IntroductionWhen using domain-centric data models as described above, there often is a need to represent the data of persistent objects, indeed entire graphs of related persistent objects, in a datastore implementation-independent , externally readable manner. The purpose of such an external representation is usually interchanging information, and often between systems of different technology, may be using a SOAP-based Web service. However, scenarios like transferring content from one (JDO) datastore to another, or migrating data from one (earlier or different) schema model to another one, equally benefit from a neutral data interchange format. These days, XML is the universally accepted data representation format for many such applications. Various ways exist to write XML data from Java objects (marshaling) and create Java objects by reading XML data (unmarshaling). 13.4.2 AlternativesIn fact, at least one Java persistence framework (Exolab JDO, which is not a Sun JDO API implementation) comes with XML marshaling and unmarshaling abilities straight out of the box and somewhat tied into its API for orthogonal object persistence. Although the JDO API itself does not offer a built-in XML marshaling and unmarshaling feature, and successfully focuses on binary datastore persistence exclusively, it is relatively easy to achieve if your application requires this. Several possible roads forward exist, and most are based on open -source or freely available XML toolkits, as we see. Before delving into practice and demonstrating a concrete running example, let us clarify that the next paragraphs are not about how to use JDO with or as an XML database, a database architecture that uses XML structure as native storage format, allowing applications to work transactionally safe with and query possibly large sets of XML data. We are simply going to see how you can marshal and unmarshal persistent JavaBean objects to and from XML, irrelevant of the underlying datastore. However, it is probably only a matter of time until major XML database vendors provide implementations to access their own respective XML datastores using the JDO API. 13.4.3 Available technologiesThe savvy Java developer by now will say, "Eh, no big deal, we can put together some code using the JAXP API using SAX or DOM and spit out or process that XML!" That's certainly true, but before you starting hacking away, consider that others have already done this job for you. The possible solutions that we can consider for this purpose are generally termed XML data binding products. Most XML data binding products fall into two categories:
Other categorizations are possible, notably around questions such as "Can any arbitrary instances be XML serialized?" or "Are XML subtleties such as exact whitespace usage or comments represented (for full object-XML round-tripping) but are of less importance for the purpose of this discussion?" However, the choice between a solution supporting the former or latter of the above approaches is one to make based on the need of your project, because both are equally applicable to JDO-based applications:
Below is a likely non-exhaustive list of some libraries that you may wish to check out. The following are generator-like solutions:
These are runtime solutions:
There are many others that are usually fairly similar. The one major difference generally is whether a predefined fixed XML language (tag and field names , and so on) is used, which is the case for JDK 1.4 long-term bean persistence and looks something like the following, which is a non-JDO related example, simply illustrating XML format: <?xml version="1.0" encoding="UTF-8"?> <java version="1.0" class="java.beans.XMLDecoder"> <object class="javax.swing.JFrame"> <void property="name"> <string>frame1</string> </void> <void property="bounds"> <object class="java.awt.Rectangle"> <int>0</int> <int>0</int> <int>200</int> <int>200</int> </object> </void> <void property="contentPane"> <void method="add"> <object class="javax.swing.JButton"> <void property="label"> <string>Hello</string> </void> </object> </void> </void> <void property="visible"> <boolean>true</boolean> </void> </object> </java> But if the solution is able to derive an XML language from the JavaBeans fields, usually with more or less configurable mappings, it leads to something like this: <?xml version="1.0" encoding="UTF-8"?> <book id="2"> <authors> <author id="3"> <name>Keiron McCammon</name> <email>mccammon@corejdo.com</email> <zipCode>10243</zipCode> </author> <author id="4"> <email>tyagi@corejdo.com</email> <name>Sameer Tyagi</name> <zipCode>31484</zipCode> </author> <author id="5"> <name>Michael Vorburger</name> <email>vorburger@corejdo.com</email> <zipCode>1234</zipCode> </author> <author id="6"> <name>Heiko Bobzin</name> <email>bobzin@corejdo.com</email> <zipCode>3391</zipCode> </author> </authors> <price>39.95</price> <title>Core Java Data Objects Book</title> </book> 13.4.4 Let's do itLet's now see some code on how to achieve this in practice. For this example, we are going to use the Betwixt library, a useful little library from the Apache Jakarta Commons project. Betwixt is in beta stage at the time of this writing, but remaining problems are likely to be sorted out rapidly . As a runtime approach solution, there is no need to write a DTD or an XML schema, or to generate any classes, and so on, as long as our persistent objects are JavaBeans, which the Author and Book classes are. Given a Book instance, jdoBook , with a few Authors associated, the XML snippet shown above is actually as simple to obtain as this line with Betwixt: BeanWriter beanWriter = new BeanWriter( System.out ); beanWriter.write( jdoBook ); Reading a snippet like this could be achieved with a few lines as follows : BeanReader beanReader = new BeanReader(); beanReader.registerBeanClass( Author.class ); beanReader.registerBeanClass( Book.class ); Book book = (Book)beanReader.parse("book.xml"); Betwixt does not require any further initialization or setup code. Although we write to a PrintStream ( System.out ) and read from a filename in the above simple example, Betwixt does also offer an API to write directly to a SAX ContentHandler . It gets slightly more interesting if you would like to write and read a collection (e.g., JDO Query result) of persistent objects with Betwixt. One way to achieve this with Betwixt is to write a simple container class like the following: public class BookCollection { private List books; public BookCollection() { books = new LinkedList(); } public BookCollection(Collection someBooks) { books = new LinkedList(someBooks); } public List getBooks() { return Collections.unmodifiableList( books ); } public void addBook(Book _book) { books.add(_book); } Betwixt (at least the latest version at the time of this writing) requires the addBook(Book book) method to understand that this top-level <BookCollection> contains <Book> objects. A more complete example scenario could look like this: It creates a few Author and Book objects, with associations from some Books to some Authors. These objects are then persisted in JDO. A real application would get some real work done at this point, and possibly lose references to these objects just created. Then we fire off a JDO Query to find all Book objects. Now we marshal all these objects into an XML representation and store them in a file. Finally, the example reads the file again and compares the unmarshaled Author and Book objects to the original objects. This simple example implemented using Betwixt or JDK 1.4 long-term bean persistence has several severe limitations:
These problems could, however, be addressed by either extending this simple example and/or using other XML data binding technologies. |