Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Monday, August 27, 2012

Java Generics and XML

In my world of .Net, I know exactly how to serialize and deserialize between Objects and an XML file.  I typically carry around functions with Generics so I can minimize my code.

I had to do something in Java, and struggled to find the exact same thing out on the web.  So I made my own set of functions using JAXB.

**************************************************

import java.io.*;

import javax.xml.*;
import javax.xml.bind.*;
import javax.xml.validation.*;

import org.xml.sax.SAXException;

/**
 * Generic Utilitarian Class 
 *
 */
public class GenericUtilities
{
/**
* Class for taking an XML file and converting it to define objects
* @param xmlFile - A valid XML file for pulling object definitions from
* @param schemaFile - 
* @param type - The class of the object to convert the XML into
* @return - The instance of the defined class, constructed from the XML
* @throws JAXBException 
* @throws FileNotFoundException
* @throws SAXException 
*/
public static T DeserializeObjects(String xmlFile, String schemaFile, Class type) throws JAXBException, FileNotFoundException, SAXException
{
// Create a JAXB context passing in the class of the object we want to marshal/unmarshal
        JAXBContext context = JAXBContext.newInstance(type);
 
// Create the unmarshaller to transform the XML back into an object
        Unmarshaller unmarshaller = context.createUnmarshaller();
 
        // Create a variable for the schema.
        Schema xmlSchema = null;
        
        if (schemaFile != null)
        {
        // Establish the schema factory for loading the schema
        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
       
        // Get a File object representation of the inputed schema
        File sf = new File(schemaFile);
       
        // Use the factory and file objects to instantiate an instance of the Schema object
        xmlSchema = factory.newSchema(sf);
        }
        
        // Set schema validation.  Null value turns validation off.
        unmarshaller.setSchema(xmlSchema);
        
        // Unmarshal the XML in the stringWriter back into an object
        Object o = unmarshaller.unmarshal(new FileInputStream(xmlFile));
        T entity = type.cast(o);
        
return entity;
}
/**
* Converts an instance object into a String containing the XML representation of that object.
* @param entity - The object to serialize into XML
* @return
* @throws JAXBException
*/
public static String SerializeObjects(T entity) throws JAXBException
{
// Create a JAXB context passing in the class of the object we want to marshal/unmarshal
        JAXBContext context = JAXBContext.newInstance(entity.getClass());
 
        // Create the marshaller, this will transform the object into XML
        Marshaller marshaller = context.createMarshaller();
 
        // Create a stringWriter to hold the XML
        StringWriter stringWriter = new StringWriter();
        
        // Marshal the javaObject and write the XML to the stringWriter
        marshaller.marshal(entity, stringWriter);
 
        return stringWriter.toString();
}
}

Tuesday, January 31, 2012

Using Saxon to embed an image's binary into Wordprocessingml (xml)

We have a process for taking an xml document (representative strictly of content) and generating a Microsoft Word document.  We leverage transforms to create a flat, single WordprocessingML file, and later leverage code to convert that to an OPC (the zip archive of DOCX).

One of our greatest challenges was getting images embedded into the document.  With the binaryData element, we knew it was possible, and our uses were in demand for it.

Because word can open the flat file, or the archive, we felt it was important to get the image binary into the XML, and into the base64 format.

In leveraging Saxon, and XSL 2.0, we were able to accomplish this.  (This functionality worked with both Saxon.Net as well as the java edition).

You’ll see below the use of defining java objects, and having the image data pulled in, and converted.

********** Stylesheet Snippet Below **********

<xsl:stylesheet
  version="2.0"
 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xlink="http://www.w3.org/1999/xlink"
 
  xmlns:msp="urn:us:gov:ic:msp:v4"
  xmlns:ism="urn:us:gov:ic:ism:v2"
         
  xmlns:file="java.io.File"
  xmlns:uri="java.net.URI"
  xmlns:url="java.net.URL"
  xmlns:javatype="http://saxon.sf.net/java-type"
  xmlns:is="java.io.InputStream"
  xmlns:bis="java.io.BufferedInputStream"
  xmlns:bos="java.io.ByteArrayOutputStream"
  xmlns:b64="sun.misc.BASE64Encoder"
   
  exclude-result-prefixes="xs xsl xlink msp file uri url javatype is bis bos b64" >

<!-- Additional xsl here -->

<xsl:template match="myImageNode" >
    <!-- Additional xsl here -->
      <ns1:binaryData>
        <!-- Using the href attribute, try and embed the image binary into the xml -->
        <xsl:call-template name="javaReadFile">
          <xsl:with-param name="fileNamePath" select="@xlink:href" />
        </xsl:call-template>
      </ns1:binaryData>
    </ns1:part>
  </xsl:template>

  <!-- Template used to embed images into a Wordml document -->
  <xsl:template name="javaReadFile">
    <xsl:param name="fileNamePath" />

    <!-- Capture the target image file into a java.net.URL object -->
    <xsl:variable name="fileUrl">
      <xsl:choose>
        <xsl:when test="not(contains($fileNamePath, '://'))"><!-- Test for cases where it is not a URL -->
          <xsl:variable name="fileObj" select="file:new(string($fileNamePath))" /><!-- Capture in a java.io.File object -->
          <xsl:variable name="fileUri" select="file:toURI($fileObj)" /><!-- Convert to a java.net.URI object -->
          <xsl:value-of select="uri:toURL($fileUri)" /><!-- Convert to a java.net.URL object -->
        </xsl:when>
        <xsl:otherwise>
          <!-- Create a new instance of the object using a default constructor -->
          <xsl:value-of select="url:new(string($fileNamePath))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

   <!-- Open the image file for reading -->
    <xsl:variable name="inStream" select="url:openStream($fileUrl) treat as javatype:java.io.InputStream" />
   
    <!-- Wrap the image file stream with a buffer stream for performance reasons -->
    <xsl:variable name="bInStream" select="bis:new($inStream) treat as javatype:java.io.BufferedInputStream" />
   
    <!-- Create a stream to hold the image binary and for encoding later  -->
    <xsl:variable name="bOutStream" select="bos:new() treat as javatype:java.io.ByteArrayOutputStream" />

    <!-- Read the image contents -->
    <xsl:call-template name="javaReadByte">
      <xsl:with-param name="inStream" select="$bInStream" />
      <xsl:with-param name="outStream" select="$bOutStream" />
    </xsl:call-template>

    <!-- Create a sun.misc.BASE64Encoder -->
    <xsl:variable name="encoder" select="b64:new()" />
   
    <!-- Output the image bytes into an encoded base64 string -->
    <xsl:value-of select="b64:encode($encoder, bos:toByteArray($bOutStream))" />

    <!-- Close the working streams -->
    <xsl:value-of select="bos:close($bOutStream)" />
    <xsl:value-of select="bis:close($bInStream)" />
  </xsl:template>

  <!-- Recursive function for reading one byte at a time from one stream and writing them into another -->
  <xsl:template name="javaReadByte">
    <xsl:param name="inStream" />
    <xsl:param name="outStream" />

    <!-- Capture the next byte into a local variable -->
    <xsl:variable name="byte" select="bis:read($inStream)" />

    <!-- Check to see if the byte represents the end of the file -->
    <xsl:if test="not($byte = -1)">
      <!-- Write the byte onto the new stream -->
      <xsl:value-of select="bos:write($outStream, $byte)"/>

      <!-- Call this template again (recursive) for reading the next byte -->
      <xsl:call-template name="javaReadByte">
        <xsl:with-param name="inStream" select="$inStream" />
        <xsl:with-param name="outStream" select="$outStream" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>