8.3 Structure Adjustment by XSLT
In the previous section, we assumed that our XML documents and application data share the same structure. Sometimes this assumption does not hold in the real world, and the technique we just saw cannot be directly applied.
Consider our purchase order application. We use the same XML document, po.xml, (see Listing 8.1) as the input to our application. In this format, one purchase order can have multiple items in it. Suppose that we are asked to feed the XML documents into a legacy application whose data model allows only one item per purchase order.2 The mapping we need is something like that shown in Figure 8.3.
Figure 8.3 Structural mismatch
There is an apparent mismatch between the input XML structure and the application data structure, so the technique we used in the previous section cannot be directly applied. How can we write a program that creates the desired Java data structure, as shown in Figure 8.3, from this input? Modifying the program in the previous section is one possibility. To do that, however, you need to temporarily store the customer data that will later be assigned to the PurchaseOrderobjects. This is not a big deal in terms of the number of lines of additional code, but you may need either a global variable or additional parameters in the unmarshal() methods.
Another way to solve this nonstraightforward mapping is to use XSLT to adjust the XML structure before processing. Suppose that the input XML has the form shown in Listing 8.10 instead of Listing 8.1. This time, the mapping is isomorphic and the technique in the previous section can be directly applied.
Listing 8.10 Transformed purchase order, chap08/mismatch/po1.xml
<?xml version="1.0" encoding="UTF-8"?> <purchaseOrders> <purchaseOrder> <name>Robert Smith</name> <customerId>788335</customerId> <address>8 Oak Avenue, New York, US</address> <partNum>872-AA"</partNum> <productName>Lawnmower</productName> <quantity>1</quantity> <USPrice>148.95</USPrice> <shipDate>2002-09-03</shipDate> </purchaseOrder> <purchaseOrder> <name>Robert Smith</name> <customerId>788335</customerId> <address>8 Oak Avenue, New York, US</address> <partNum>926-AA</partNum> <productName>Baby Monitor</productName> <quantity>1</quantity> <USPrice>39.98</USPrice> <shipDate>2002-08-21</shipDate> </purchaseOrder> </purchaseOrders>
How can we obtain Listing 8.10 from Listing 8.1? The answer is to use XSLT. XSLT is designed for this sort of transformation. It is a powerful language to do such things as:
Extracting <item>elements from inside a purchaseOrder/itemselement
Adding child elements of the <customer>element to each <item>element
The XSLT script mismatch/flatten.xsl(see Listing 8.11) does this transformation. Note that this transformation can be called from a Java program using the JAXP API, as we discussed in Section 7.2.2 of Chapter 7, so the extra generation and parsing are eliminated.
Listing 8.11 XSLT transformation script, chap08/mismatch/flatten.xsl
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" encoding="UTF-8"/> <xsl:template match="/"> <purchaseOrders> <xsl:apply-templates select="purchaseOrder/items"/> </purchaseOrders> </xsl:template> <xsl:template match="item"> <purchaseOrder> <xsl:copy-of select="/purchaseOrder/customer/name"/> <xsl:copy-of select="/purchaseOrder/customer/customerId"/> <xsl:copy-of select="/purchaseOrder/customer/address"/> <partNum><xsl:value-of select="@partNum"/></partNum> <xsl:copy-of select="productName"/> <xsl:copy-of select="quantity"/> <xsl:copy-of select="USPrice"/> <xsl:copy-of select="shipDate"/> </purchaseOrder> </xsl:template> </xsl:stylesheet>
Can you see the usefulness of XSLT in mapping from XML to an application-specific data structure? This technique also applies to applications that need to process logically equivalent but different DTD documents. For example, if your application is required to process both types of purchase orders as in Listings 8.1 and 8.10, a single application program, combined with an appropriate XSLT stylesheet, will be enough for processing both. This situation frequently occurs when there are multiple versions of DTDs for essentially the same set of documents.
So far we focused on cases where mappings are relatively straightforward. In other words, your application data structure has essentially a tree shape, and it reflects the structure of marshaled XML documents fairly well. Some developers argue that 80% of all application development is indeed of this type, so 80% of the time you can have a straightforward mapping as we saw in Section 8.2, especially when we use the XSLT technique for adjusting the structure. Having a straightforward mapping is even more important if you use a software tool that does the mapping automatically. In Chapter 15, we will see such software tools.
Of course, there is always the remaining 20%. When we are told to develop such an application, we need to write our own custom mapping code. Even in such cases, there are certain patterns that may be useful to you. The next three sections show some of these patterns that we encounter most frequently.