The Conversion

   

The parser generates only the intermediate XML-ized format. This format is just that, a temporary step in the conversion. It would not be sensible to use it as the real XML order. Therefore, the next step is to transform the intermediate format into the real XML order (see Listing 6.7).

Listing 6.7 orders.xml
 <?xml version="1.0"?> <Order confirm="true">    <Date>2000-03-10</Date>    <Reference>AGL153</Reference>    <DeliverBy>2000-04-10</DeliverBy>    <Buyer>       <Name>PLAYFIELD BOOKS</Name>       <Address>          <Street>34 FOUNTAIN SQUARE PLAZA</Street>          <Locality>CINCINNATI</Locality>          <PostalCode>45202</PostalCode>          <Region>OH</Region>          <Country>US</Country>       </Address>    </Buyer>    <Seller>       <Name>QUE</Name>       <Address>          <Street>201 WEST 103RD STREET</Street>          <Locality>INDIANAPOLIS</Locality>          <PostalCode>46290</PostalCode>          <Region>IN</Region>          <Country>US</Country>       </Address>    </Seller>    <Lines>       <Product>          <Code type="ISBN">0789722429</Code>          <Description>XML by Example</Description>          <Quantity>5</Quantity>          <Price>24.99</Price>       </Product>       <Product>          <Code type="ISBN">0789724308</Code>          <Description>Applied XML Solutions</Description>          <Quantity>10</Quantity>          <Price>42.50</Price>       </Product>    </Lines> </Order> 

The Style Sheet

The main transformation is the responsibility of the XSLT style sheet in Listing 6.8.

Listing 6.8 edi2xml.xsl
 <?xml version="1.0"  encoding="ISO-8859-1"?> <xsl:stylesheet    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"    xmlns:axslt="http://xml.apache.org/xslt"    xmlns:psol="http://www.psol.com/xsledi/extensions"    extension-element-prefixes="psol"    version="1.0"> <axslt:component prefix="psol"                  functions="lookupDescription"                  elements="register">    <axslt:script lang="javaclass"                  src="com.psol.xsledi.Extensions"/> </axslt:component> <xsl:output method="xml"/> <xsl:template match="/Message">    <psol:register isbn="0789722429"                   title="XML by Example"/>    <psol:register isbn="0789724308"                   title="Applied XML Solutions"/>   <Order>       <xsl:attribute name="confirm">          <xsl:variable name="ack"                        select="Segment[@tag='BGM']/Simple[2]"/>          <xsl:value-of select="$ack != 'NA'"/>       </xsl:attribute>       <xsl:call-template name="Date"/>       <xsl:call-template name="Reference"/>       <xsl:call-template name="DeliverBy"/>       <xsl:call-template name="Buyer"/>       <xsl:call-template name="Seller"/>       <xsl:call-template name="Lines"/>    </Order> </xsl:template> <xsl:template name="format-date">    <xsl:param name="date"/>    <xsl:value-of select="substring($date,1,4)"/>    <xsl:text>-</xsl:text>    <xsl:value-of select="substring($date,5,2)"/>    <xsl:text>-</xsl:text>    <xsl:value-of select="substring($date,7,2)"/> </xsl:template> <xsl:template name="Date">    <xsl:variable name="date"                  select="Segment[@tag='DTM'and                         child::Composite[Simple[1]='137']]"/>    <Date>       <xsl:call-template name="format-date">          <xsl:with-param name="date"                          select="$date/Composite[1]/Simple[2]"/>       </xsl:call-template>    </Date> </xsl:template> <xsl:template name="Reference">    <Reference>       <xsl:value-of select="Segment[@tag='BGM']/Simple[1]"/>    </Reference> </xsl:template> <xsl:template name="DeliverBy">    <xsl:variable name="date"                  select="Segment[@tag='DTM'and                          child::Composite[Simple[1]='61']]"/>    <xsl:if test="$date">       <DeliverBy>          <xsl:call-template name="format-date">             <xsl:with-param              name="date"              select="$date/Composite[1]/Simple[2]"/>          </xsl:call-template>       </DeliverBy>    </xsl:if> </xsl:template> <xsl:template name="Address">    <xsl:param name="address"/>    <Name>       <xsl:for-each select="$address/Composite[3]/Simple">          <xsl:value-of select="."/>          <xsl:if test="not(position()=last())">             <xsl:text> </xsl:text>          </xsl:if>       </xsl:for-each>    </Name>    <Address>       <Street>          <xsl:for-each select="$address/Composite[4]/Simple">             <xsl:value-of select="."/>             <xsl:if test="not(position()=last())">                <xsl:text> </xsl:text>             </xsl:if>          </xsl:for-each>       </Street>       <Locality>          <xsl:value-of select="$address/Simple[2]"/>       </Locality>       <PostalCode>          <xsl:value-of select="$address/Simple[4]"/>       </PostalCode>       <xsl:variable name="region"                     select="$address/Simple[3]"/>       <xsl:if test="string-length($region) != 0">          <Region>             <xsl:value-of select="$region"/>          </Region>       </xsl:if>       <Country>          <xsl:value-of select="$address/Simple[5]"/>       </Country>    </Address> </xsl:template> <xsl:template name="Buyer">    <xsl:variable name="buyer"                  select="Segment[@tag='NAD'and                          child::Simple[1]='BY']"/>    <Buyer>       <xsl:call-template name="Address">          <xsl:with-param name="address" select="$buyer"/>       </xsl:call-template>    </Buyer> </xsl:template> <xsl:template name="Seller">    <xsl:variable name="seller"                  select="Segment[@tag='NAD'and                          child::Simple[1]='SE']"/>    <Seller>       <xsl:call-template name="Address">          <xsl:with-param name="address" select="$seller"/>       </xsl:call-template>    </Seller> </xsl:template> <xsl:template name="Lines">    <Lines>       <xsl:for-each select="Segment[@tag='LIN']">          <xsl:variable name="code"                        select="following-sibling::Segment [@tag='PIA']/Composite[1]/ graphics/ccc.gif Simple[1]"/>          <xsl:variable name="type"                        select="following-sibling::Segment [@tag='PIA']/Composite[1]/ graphics/ccc.gif Simple[2]"/>          <Product>             <Code>                <xsl:attribute name="type">                   <xsl:choose>                      <xsl:when test="$type = 'IS'">                         <xsl:text>ISSN</xsl:text>                      </xsl:when>                      <xsl:otherwise>                         <xsl:text>ISBN</xsl:text>                      </xsl:otherwise>                   </xsl:choose>                </xsl:attribute>                <xsl:value-of select="$code"/>             </Code>             <Description>                <xsl:value-of select="psol:lookupDescription( string($code), graphics/ccc.gif string($type))"/>             </Description>             <Quantity>                <xsl:value-of select="following-sibling::Segment [@tag='QTY']/Composite[1]/ graphics/ccc.gif Simple[2]"/>             </Quantity>             <Price>                <xsl:value-of select="following-sibling::Segment [@tag='PRI']/Composite[1]/ graphics/ccc.gif Simple[2]"/>             </Price>          </Product>       </xsl:for-each>    </Lines> </xsl:template> </xsl:stylesheet> 

The style sheet creates the XML document by extracting information from the EDIFACT document and placing it in the right order. We'll look at the register elements in a moment:

 <xsl:template match="/Message">      <psol:register isbn="0789722429"                     title="XML by Example"/>      <psol:register isbn="0789724308"                     title="Applied XML Solutions"/>      <Order>         <xsl:attribute name="confirm">            <xsl:variable name="ack"                          select="Segment[@tag='BGM']/Simple[2]"/>            <xsl:value-of select="$ack != 'NA'"/>         </xsl:attribute>         <xsl:call-template name="Date"/>         <xsl:call-template name="Reference"/>         <xsl:call-template name="DeliverBy"/>         <xsl:call-template name="Buyer"/>         <xsl:call-template name="Seller"/>         <xsl:call-template name="Lines"/>      </Order>   </xsl:template>] 

In these templates, the selection criteria is relatively complex. Look at the template for Date as an example. It extracts data with a combination of the element name ( Segment ), one of its attributes ( @tag='DTM' ), and the value of one of its children ( child::Composite[Simple[1]='137']] ).

Date needs this complex, multi-level selection to deal with EDIFACT qualifiers. You will recall that EDIFACT qualifiers are used to encode relationships between segments, so they must be read to re-create the structure of the document.

Fortunately, the EDIFACT parser doesn't need to deal with these; otherwise, it would have been more complicated. The parser performs a minimalist translation, and the more complex processing is relegated to the XSLT style sheet.

Incidentally, this illustrates why the XML-ized EDIFACT order should remain an intermediate format. It is more complex to manipulate than it needs to be. Simplifying it makes more sense:

 <xsl:template name="Date">      <xsl:variable name="date"                    select="Segment[@tag='DTM'and                           child::Composite[Simple[1]='137']]"/>      <Date>         <xsl:call-template name="format-date">            <xsl:with-param name="date"                            select="$date/Composite[1]/Simple[2]"/>         </xsl:call-template>      </Date>   </xsl:template> 

The most complex template is probably the template for the Lines element because it groups information from several segments. Recall that, in the previous chapter, the equivalent template broke the Product element into segments.

Unfortunately, because an EDIFACT message is a list of segments, it is not easy to extract the information from the various segments. The least one could say is that XSLT is not optimized for relatively flat structures; it was designed for more hierarchical structures.

Note

This illustrates a common problem when dealing with legacy formats (whether EDIFACT or another format): Modern tools are not optimized to manipulate them.


The best solution is to use the next-sibling axis to retrieve specific segments after the current one:

 <xsl:template name="Lines">      <Lines>         <xsl:for-each select="Segment[@tag='LIN']">            <xsl:variable name="code"                        select="following-sibling::Segment [@tag='PIA']/Composite[1]/ graphics/ccc.gif Simple[1]"/>            <xsl:variable name="type"                          select="following-sibling::Segment [@tag='PIA']/Composite[1]/ graphics/ccc.gif Simple[2]"/>            <Product>               <Code>                  <xsl:attribute name="type">                     <xsl:choose>                        <xsl:when test="$type = 'IS'">                           <xsl:text>ISSN</xsl:text>                        </xsl:when>                        <xsl:otherwise>                           <xsl:text>ISBN</xsl:text>                        </xsl:otherwise>                     </xsl:choose>                  </xsl:attribute>                  <xsl:value-of select="$code"/>               </Code>               <Description>                  <xsl:value-of select="psol:lookupDescription( string($code), graphics/ccc.gif string($type))"/>               </Description>               <Quantity>                  <xsl:value-of select="following-sibling::Segment [@tag='QTY']/ graphics/ccc.gif Composite[1]/Simple[2]"/>               </Quantity>               <Price>                  <xsl:value-of select="following-sibling::Segment [@tag='PRI']/ graphics/ccc.gif Composite[1]/Simple[2]"/>               </Price>            </Product>         </xsl:for-each>      </Lines>   </xsl:template> 

Warning

For documents with more complex structures, grouping segments using the following-sibling axis is not possible. The solution is either to use XSLT extensions (see the next section) or to enhance the parser to produce a more structured intermediate format.


Extensions

The style sheet uses an XSLT extension to deal with product descriptions. The XSL standard proposes a mechanism to recognize non-standard functions and elements.

However, the XSL standard doesn't specify how to write these extensions. This is left to the developers of the processor.

In this chapter, we use Xalan extensions , so we'll use the Xalan extension mechanism.

The XSL standard marks extensions with a specific namespace. The namespace must be registered with the extension-element-prefixes attribute.

In the style sheet, extensions are located in the http://www.psol.com/xsledi/extensions namespace:

 <xsl:stylesheet      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"      xmlns:axslt="http://xml.apache.org/xslt"      xmlns:psol="http://www.psol.com/xsledi/extensions"      extension-element-prefixes="psol"      version="1.0"> 

Xalan declares the extensions with the component element , which is Xalan specific. We can declare one new element, register , and one new function, lookupDescription() . Both are implemented in the Java com.psol.xsledi.Extensions :

 <axslt:component prefix="psol"                    functions="lookupDescription"                    elements="register">      <axslt:script lang="javaclass"                    src="com.psol.xsledi.Extensions"/>   </axslt:component> 

The lookupDescription() retrieves a book title (product description) from its ISBN. register , on the other hand, initializes the list of titles (an alternative is to read them from a database):

 <psol:register isbn="0789722429"                  title="XML by Example"/>   <psol:register isbn="0789724308"                  title="Applied XML Solutions"/> 

The implementation of register and lookupDescription() is shown in Listing 6.9. Xalan uses the Bean Scripting Framework (BSF) to access the implementation, so the functions could have been written in any other BSF-compliant language, such as JPython (which can be downloaded from http://www.jpython.org ) or JavaScript (which can be downloaded from http://www.mozilla.org/ rhino ).

Listing 6.9 Extensions.java
 package com.psol.xsledi; import java.util.Hashtable; import org.apache.xalan.xslt.*; public class Extensions {    protected Hashtable isbns = new Hashtable();    public void register(XSLProcessorContext context,                         ElemExtensionCall extElem)    {       String isbn = extElem.getAttribute("isbn"),              title = extElem.getAttribute("title");       isbns.put(isbn,title);    }    public Extensions()       {}    public String lookupDescription(String code,String type)    {       String desc = type.equals("IB") ?                     (String)isbns.get(code) : null;       return null != desc ? desc : "unknown";    } } 

Edifact2XML

The starting point for the application is Edifact2XML (see Listing 6.10). It parses the EDIFACT stream into a character array and applies the style sheet to the result.

Listing 6.10 Edifact2XML.java
 package com.psol.xsledi; import java.io.*; import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import org.apache.xalan.xslt.*; public class Edifact2XML {    public static final String PARSER_NAME =       "org.apache.xerces.parsers.SAXParser";    public static void main(String args[])       throws UnexpectedTokenException, SAXException,              IOException, ClassNotFoundException,              IllegalAccessException, InstantiationException    {       EdifactStructure structure = new EdifactStructures);       Parser parser = ParserFactory.makeParser(PARSER_NAME);       parser.setDocumentHandler(structure);       parser.parse("edifactstructure.xml");       CharArrayWriter writer = new CharArrayWriter();       EdifactParser eparser = new EdifactParser(structure);       eparser.setWriter(writer);       eparser.parse(args[0]);       writer.close();       char[] carray = writer.toCharArray();       Reader reader = new CharArrayReader(carray);       InputStream sxsl = new FileInputStream(args[1]);       OutputStream sout = new FileOutputStream(args[2]);       XSLTProcessor processor =          XSLTProcessorFactory.getProcessor();       XSLTInputSource in = new XSLTInputSource(reader),                       xsl = new XSLTInputSource(sxsl);       XSLTResultTarget out = new XSLTResultTarget(sout);       processor.process(in,xsl,out);    } } 
   


Applied XML Solutions
Applied XML Solutions
ISBN: 0672320541
EAN: 2147483647
Year: 1999
Pages: 142

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net