Using XSLT - beckchr/staxon GitHub Wiki

StAXON makes JSON look like XML on the Java side. One benefit from this is that you can integrate JSON with powerful XML-related technologies for free. XSLT Transformations (XSLT) is a widely used standard and can be easily adopted for use with JSON.

You can use XSLT with JSON as transformation source, target or both.

Twitter Example

Our sample request we'll send over to Twitter is http://search.twitter.com/search.json?q=%23XSLT to search for tweets containing the #XSLT hashtag. Then, we'll use StAXON and XSLT to transform the JSON to XML on the fly.

First of all, we need an XSLT stylesheet.

Stylesheet

For the sake of simplicity, our stylessheet only maps a subset of the properties provided by the Twitter Search API. The root element searchResults takes attributes query and completedIn. It contains a sequence of results elements with attributes createdAt and fromUser, and the tweeted text as content.

stylesheet.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:template match="searchResults">
		<searchResults query="{query}" completedIn="{completed_in}">
			<xsl:for-each select="results">
				<result createdAt="{created_at}" fromUser="{from_user}">
					<xsl:apply-templates select="text"/>
				</result>
			</xsl:for-each>
		</searchResults>
	</xsl:template>
</xsl:stylesheet>

Demo

We are now ready to create and run some sample code.

TwitterDemo.java

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stax.StAXResult;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamSource;

import de.odysseus.staxon.json.JsonXMLConfig;
import de.odysseus.staxon.json.JsonXMLConfigBuilder;
import de.odysseus.staxon.json.JsonXMLInputFactory;
import de.odysseus.staxon.xml.util.PrettyXMLStreamWriter;

public class TwitterDemo {
	/**
	 * Transform JSON returned from Twitter to XML using XSLT.
	 * @param args ignored
	 * @throws TransformerException
	 * @throws XMLStreamException
	 */
	public static void main(String[] args) throws TransformerException, XMLStreamException, IOException {
		/*
		 * The JSON object returned from twitter does not include the "searchResults" root,
		 * so we specify it as "virtual" root element. When reading, the virtual root will be
		 * added to the stream. When writing, the "virtual" root will be removed from the stream.
		 */
		JsonXMLConfig config = new JsonXMLConfigBuilder().
				virtualRoot("searchResults").
				prettyPrint(true).
				build();

		/*
		 * Search for tweets containing the #XSLT" hashtag.
		 */
		InputStream input = new URL("http://search.twitter.com/search.json?q=%23XSLT").openStream();

		/*
		 * Create source (JSON).
		 */
		XMLStreamReader reader = new JsonXMLInputFactory(config).createXMLStreamReader(input);
		Source source = new StAXSource(reader);

		/*
		 * Create result (XML).
		 */
		XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(System.out);
		Result result = new StAXResult(new PrettyXMLStreamWriter(writer));

		/*
		 * Transform (JSON to XML).
		 */
		Source stylesheet = new StreamSource(TwitterDemo.class.getResourceAsStream("stylesheet.xsl"));
		TransformerFactory.newInstance().newTransformer(stylesheet).transform(source, result);

		/*
		 * As per StAX specification, XMLStreamReader.close() doesn't close the
		 * underlying stream.
		 */
		input.close();
	}
}

Running the sample will produce something like this:

<searchResults query="%23XSLT" completedIn="0.055">
	<result createdAt="Sat, 24 Sep 2011 14:52:55 +0000" fromUser="me">Transforming #JSON with #XSLT...</result>
	...
</searchResults>

Transforming to JSON

What if we wanted to transform to equivalent JSON instead? Let's try and change the above sample code to use a JSON output factory:

	/*
	 * Create result (JSON).
	 */
	XMLStreamWriter writer = new JsonXMLOutputFactory(config).createXMLStreamWriter(System.out);
	Result result = new StAXResult(writer);

Now, running this yields

{
  "searchResults" : {
    "@query" : "%23XSLT",
    "@completedIn" : "0.197",
    "result" : {
      "@createdAt" : "Sat, 24 Sep 2011 14:52:55 +0000",
      "@fromUser" : "me",
      "$" : "Transforming #JSON with #XSLT and #StAXON http://t.co/3COyAl5g"
    },
    "result" : {
      ...
    },
    ...
  }
}

Not bad, but... the array is missing around the result objects!

StAXON lets you fix this using two methods:

  • trigger array starts using the xml-multiple processing instruction (recommended)
  • let StAXON automatically determine arrays by setting the JsonXMLOutputFactory.PROP_AUTO_ARRAY (or JsonXMLConfig.autoArray) property to true (not recommended)

So, lets modify our stylesheet to include the 'magic' processing instruction:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:template match="searchResults">
		<searchResults query="{query}" completedIn="{completed_in}">
			<xsl:processing-instruction name="xml-multiple">result</xsl:processing-instruction>
			<xsl:for-each select="results">
				<result createdAt="{created_at}" fromUser="{from_user}">
					<xsl:apply-templates select="text"/>
				</result>
			</xsl:for-each>
		</searchResults>
	</xsl:template>
</xsl:stylesheet>

Now we get

{
  "searchResults" : {
    "@query" : "%23XSLT",
    "@completedIn" : "0.133",
    "result" : [ {
      "@createdAt" : "Sat, 24 Sep 2011 14:52:55 +0000",
      "@fromUser" : "me",
      "$" : "Transforming #JSON with #XSLT and #StAXON http://t.co/3COyAl5g"
    }, {
      ...
    } ]
  }
}

Better!

⚠️ **GitHub.com Fallback** ⚠️