<?xml version="1.0"?> | |
<!-- | |
~ Licensed to the Apache Software Foundation (ASF) under one | |
~ or more contributor license agreements. See the NOTICE file | |
~ distributed with this work for additional information | |
~ regarding copyright ownership. The ASF licenses this file | |
~ to you under the Apache License, Version 2.0 (the | |
~ "License"); you may not use this file except in compliance | |
~ with the License. You may obtain a copy of the License at | |
~ | |
~ http://www.apache.org/licenses/LICENSE-2.0 | |
~ | |
~ Unless required by applicable law or agreed to in writing, | |
~ software distributed under the License is distributed on an | |
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
~ KIND, either express or implied. See the License for the | |
~ specific language governing permissions and limitations | |
~ under the License. | |
--> | |
<document xmlns="http://maven.apache.org/XDOC/2.0" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd"> | |
<properties> | |
<title>JSON Support in Axis2</title> | |
</properties> | |
<body> | |
<h1>JSON Support in Axis2</h1> | |
<section name="Introduction"> | |
<p>Update: This documentation represents early forms of JSON | |
conventions, Badgerfish and Mapped. GSON support was added a few | |
years later. Moshi support is now included as an alternative to | |
GSON. For users of JSON seeking modern features, see <a href= | |
"json_support_gson.html">JSON Support Guide.</a>. For users of | |
JSON and Spring Boot, see the sample application in the <a href= | |
"json-springboot-userguide.html">JSON and Spring Boot User's Guide.</a> | |
</p> | |
<p>This document explains the JSON support implementation in Axis2. | |
It includes an introduction to JSON, an outline as to why JSON | |
support is useful to Axis2 and how it should be used. This document | |
also provides details on test cases and samples.</p> | |
</section> | |
<section name="What is JSON?"> | |
<p><a href="http://www.json.org/">JSON</a> (Java Script Object | |
Notation) is another data exchangeable format like XML, but more | |
lightweight and easily readable. It is based on a subset of the | |
JavaScript language. Therefore, JavaScript can understand JSON, and | |
it can make JavaScript objects by using JSON strings. JSON is based | |
on key-value pairs and it uses colons to separate keys and values. | |
JSON doesn't use end tags, and it uses braces (curly brackets) to | |
enclose JSON Objects.</p> | |
<p><font size="3">e.g. <font size="2"><root><test>json | |
object</test></root> == | |
{{json object}}</font></font></p> | |
<p>When it comes to converting XML to JSON and vice versa, there | |
are two major conventions, one named "<a href= | |
"http://www.sklar.com/badgerfish/">Badgerfish</a>" and the other, | |
Mapped. The main difference | |
between these two conventions exists in the way they map XML | |
namespaces into JSON.</p> | |
<p><font size="3">e.g. <font size="2"><xsl:root | |
xmlns:xsl="http://foo.com"><data>my json | |
string</data></xsl:root></font></font></p> | |
<p>This XML string can be converted into JSON as follows.</p> | |
<p><b>Using Badgerfish</b></p> | |
<p><font size= | |
"2">{"xsl:root":{"@xmlns":{"xsl":"http://foo.com"},"data":{"$":"my | |
json string"}}}</font></p> | |
<p><b>Using Mapped</b></p> | |
<p>If we use the namespace mapping as http://foo.com -> foo</p> | |
<p><font size="2">{"foo.root":{"data":"my json string"}}</font></p> | |
</section> | |
<section name="Why JSON Support for Axis2?"> | |
<p><a href="../index.html">Apache Axis2</a> is a Web | |
services stack that delivers incoming messages into target | |
applications. In most cases, these messages are SOAP messages. In | |
addition, it is also possible to send REST messages through Axis2. | |
Both types of messages use XML as their data exchangeable format. | |
So if we can use XML as a format, why use JSON as another | |
format?</p> | |
<p>There are many advantages of implementing JSON support in Axis2. | |
Mainly, it helps the JavaScript users (services and clients written | |
in JavaScript) to deal with Axis2. When the service or the client | |
is in JavaScript, it can use the JSON string and directly build | |
JavaScript objects to retrieve information, without having to build | |
the object model (OMElement in Axis2). Also, JavaScript services | |
can return the response through Axis2, just as a JSON string can be | |
shipped in a JSONDataSource.</p> | |
<p>Other than for that, there are some extra advantages of using | |
JSON in comparison to XML. Although the conversation | |
XML or JSON? is still a hot topic, | |
many people accept the fact that JSON can be passed and built more | |
easily by machines than XML.</p> | |
</section> | |
<section name="How to use JSON in Axis2"> | |
<p>At the moment JSON doesn't have a standard and unique content | |
type. <tt>application/json</tt> (this is | |
the content type which is approved in the <a href= | |
"http://www.ietf.org/rfc/rfc4627.txt">JSON RFC</a>), | |
<tt>text/javascript</tt> and | |
<tt>text/json</tt> are some of the commonly | |
used content types for JSON. Fortunately, in Axis2, the user | |
has the freedom of specifying the content type to use.</p> | |
<subsection name="Configuring axis2.xml"> | |
<p>First of all, you need to map the appropriate message formatters and builders to the | |
content type you are using in the <tt>axis2.xml</tt> file. This applies both the to | |
client side and the server side.</p> | |
<p>E.g., if you are using the | |
Mapped convention with the content | |
type <tt>application/json</tt>, add the following declaration:</p> | |
<pre><![CDATA[ | |
<messageFormatters> | |
<messageFormatter contentType="application/json" | |
class="org.apache.axis2.json.JSONMessageFormatter"/> | |
<!-- more message formatters --> | |
</messageFormatters> | |
<messageBuilders> | |
<messageBuilder contentType="application/json" | |
class="org.apache.axis2.json.JSONOMBuilder"/> | |
<!-- more message builders --> | |
</messageBuilders> | |
]]></pre> | |
<p>If you are using the | |
Badgerfish convention with the | |
content type <tt>text/javascript</tt>, add:</p> | |
<pre><![CDATA[ | |
<messageFormatters> | |
<messageFormatter contentType="text/javascript" | |
class="org.apache.axis2.json.JSONBadgerfishMessageFormatter"> | |
<!-- more message formatters --> | |
</messageFormatters> | |
<messageBuilders> | |
<messageBuilder contentType="text/javascript" | |
class="org.apache.axis2.json.JSONBadgerfishOMBuilder"/> | |
<!-- more message builders --> | |
</messageBuilders> | |
]]></pre> | |
</subsection> | |
<subsection name="Client-side configuration"> | |
<p>On the client side, make the ConfigurationContext by reading the | |
axis2.xml in which the correct mappings are given.</p> | |
<p>e.g.</p> | |
<pre> | |
File configFile = new File("test-resources/axis2.xml"); | |
configurationContext = ConfigurationContextFactory | |
.createConfigurationContextFromFileSystem(null, configFile.getAbsolutePath()); | |
.......... | |
ServiceClient sender = new ServiceClient(configurationContext, null); | |
</pre> | |
<p>Set the <i>MESSAGE_TYPE</i> option with exactly the same content | |
type you used in the axis2.xml.</p> | |
<p>e.g. If you use the content type | |
application/json,</p> | |
<pre> | |
Options options = new Options(); | |
options.setProperty(Constants.Configuration.MESSAGE_TYPE, "application/json"); | |
//more options | |
//................... | |
ServiceClient sender = new ServiceClient(configurationContext, null); | |
sender.setOptions(options); | |
</pre> | |
<p>If you are sending a request to a remote service, you have to | |
know the exact JSON content type that is used by that service, and | |
you have to use that content type in your client as well.</p> | |
<p>HTTP POST is used as the default method to send JSON messages | |
through Axis2, if the HTTP method is not explicitly set by the | |
user. But if you want to send JSON in HTTP GET method as a | |
parameter, you can do that by just setting an option on the client | |
side.</p> | |
<p>e.g.</p> | |
<code>options.setProperty(Constants.Configuration.HTTP_METHOD, | |
Constants.Configuration.HTTP_METHOD_GET);</code> | |
<p>Here, the Axis2 receiving side (JSONOMBuilder) builds the | |
OMElement by reading the JSON string which is sent as a parameter. | |
The request can be made even through the browser.</p> | |
<p>e.g. Sample JSON request through HTTP GET. The JSON message is | |
encoded and sent.</p> | |
<code>GET | |
/axis2/services/EchoXMLService/echoOM?query=%7B%22echoOM%22:%7B%22data%22:%5B%22my%20json%20string%22,%22my%20second%20json%20string%22%5D%7D%7D | |
HTTP/1.1</code> | |
</subsection> | |
<subsection name="Server-side configuration"> | |
<p>Since Badgerfish defines a 1-to-1 transformation between JSON and XML, no additional configuration | |
is required on the server side if that convention is used. Any service deployed into Axis2 will work | |
out of the box.</p> | |
<p>On the other hand, if the Mapped JSON convention is used, then Axis2 needs to know the mappings | |
between XML namespaces and JSON "namespaces" in order to translate messages from JSON | |
into XML representations and vice-versa. To use the Mapped convention with a service deployed into Axis2, | |
add a <tt>xmlToJsonNamespaceMap</tt> property with these mappings to the <tt>services.xml</tt> file for that service, as | |
shown in the following example:</p> | |
<pre><![CDATA[ | |
<service name="..."> | |
... | |
<parameter name="xmlToJsonNamespaceMap"> | |
<mappings> | |
<mapping xmlNamespace="http://example.org/foo" jsonNamespace=""/> | |
<mapping xmlNamespace="http://example.org/bar" jsonNamespace="bar"/> | |
</mappings> | |
</parameter> | |
... | |
</service> | |
]]></pre> | |
</subsection> | |
</section> | |
<section name="How the JSON implementation works - Architecture"> | |
<subsection name="Introduction"> | |
<p>The Axis2 architecture is based on the assumption that any message flowing through | |
the Axis2 runtime is representable as a SOAP infoset, i.e. as XML wrapped in a SOAP | |
envelope. Conceptually, the two message builders <code>JSONOMBuilder</code> and | |
<code>JSONBadgerfishOMBuilder</code> convert incoming messages from JSON to XML and | |
the two message formatters <code>JSONMessageFormatter</code> and <code>JSONBadgerfishMessageFormatter</code> | |
convert outgoing messages from XML to JSON. Axis2 doesn't implement its own JSON parser and serializer, and | |
instead relies on <a href="http://jettison.codehaus.org/">Jettison</a> to do the JSON<->XML conversions.</p> | |
<p>On the server side the XML for an incoming | |
message is typically converted to Java objects by a databinding (such as ADB or JAX-WS) | |
before the invocation of the service implementation. In the same way, the Java object returned by the | |
service implementation is converted to XML. In the case we are interested in, that XML is then converted | |
by the message formatters to JSON. The usage of an intermediate XML representation is the reason why | |
JSON can be enabled on any service deployed in Axis2.</p> | |
<p>It is important to note that the explanation given in the previous two paragraphs is only valid from | |
a conceptual point of view. The actual processing model is more complicated. In the next two sections | |
we will explain in detail how Axis2 processes incoming and outgoind JSON messages.</p> | |
</subsection> | |
<subsection name="Processing of incoming JSON messages"> | |
<p>Axis2 relies on <a href="http://ws.apache.org/axiom/">Apache Axiom</a> as its XML object model. Although | |
Axiom has a DOM like API, it also has several advanced features that enable Axis2 to avoid | |
building a complete object model representation of the XML message. This is important for performance | |
reasons and distinguishes Axis2 from previous generation SOAP stacks. To leverage these features, the | |
JSON message builders create a SOAP envelope the body of which contains a single <code>OMSourcedElement</code>.</p> | |
<p>An <code>OMSourcedElement</code> is a special kind of <code>OMElement</code> that wraps an arbitrary | |
Java object that can be converted to XML in a well defined way. More precisely, the Java object as well as the logic | |
to convert the object to XML are encapsulated in an <code>OMDataSource</code> instance and it is that | |
<code>OMDataSource</code> instance that is used to create the <code>OMSourcedElement</code>. | |
For JSON, the <code>OMDataSource</code> implementation is <code>JSONDataSource</code> or <code>JSONBadgerfishDataSource</code>, | |
depending on the convention being used. The base class (<code>AbstractJSONDataSource</code>) of these two classes | |
actually contains the code that invokes Jettison to perform the JSON to XML conversion.</p> | |
<p>An <code>OMSourcedElement</code> still behaves like a normal <code>OMElement</code>. In particular, if the | |
element is accessed using DOM like methods, then Axiom will convert the data encapsulated by | |
the <code>OMDataSource</code> on the fly to an object model representation. This process is called <i>expansion</i> of the | |
<code>OMSourcedElement</code>. However, the <code>OMDataSource</code> API is designed such that the conversion to | |
XML is always done using a streaming API: either the <code>OMDataSource</code> produces an <code>XMLStreamReader</code> | |
instance from which the XML representation can be read (this is the case for JSON and the <code>XMLStreamReader</code> implementation | |
is actually provided by Jettison) or it serializes the XML representation to an <code>XMLStreamWriter</code>. | |
Because of this, expansion of the <code>OMSourcedElement</code> is often not necessary, so that the overhead of | |
creating an object model representation can usually be avoided. E.g. a databinding will typically consume the message by requesting an | |
<code>XMLStreamReader</code> for the element in the SOAP body, and this doesn't require expansion of the | |
<code>OMSourcedElement</code>. In this case, the databinding pulls the XML data almost directly from the | |
underlying Jettison <code>XMLStreamReader</code> and no additional Axiom objects are created.</p> | |
<p>Actually here again, things are slightly more complicated because in order to dispatch to the right | |
operation, Axis2 needs to determine the name of the element in the body. Since the name is not known | |
in advance, that operation requires expansion of the <code>OMSourcedElement</code>. However, at this point | |
none of the children of the <code>OMSourcedElement</code> will be built. Fortunately the databindings | |
generally request the <code>XMLStreamReader</code> with caching turned off, so that the child nodes will never be | |
built. Therefore the conclusion of the previous paragraph remains valid: processing the message with a databinding | |
will not create a complete object model representation of the XML.</p> | |
<p>Usage of an <code>OMSourcedElement</code> also solves another architectural challenge posed by | |
the Mapped JSON convention: the JSON payload can only be converted to XML if the namespace mappings | |
are known. Since they are defined per service, they are only known after the incoming message has been | |
dispatched and the target service has been identified. This typically occurs | |
in <code>RequestURIBasedDispatcher</code>, which is executed after | |
the message builder. This means that <code>JSONOMBuilder</code> cannot actually perform the conversion. | |
Usage of an <code>OMSourcedElement</code> avoids this issue because the conversion is done lazily when | |
the <code>OMSourcedElement</code> is first accessed, and this occurs after <code>RequestURIBasedDispatcher</code> | |
has been executed.</p> | |
<p>Another advantage of using <code>OMSourcedElement</code> is that a JSON aware service could directly process | |
the JSON payload without going through the JSON to XML conversion. That is possible because the <code>OMDataSource</code> | |
simply keeps a reference to the JSON payload and this reference is accessible to JSON aware code.</p> | |
</subsection> | |
<subsection name="Processing of outgoing messages"> | |
<p>For outgoing messages, the two JSON message formatters <code>JSONMessageFormatter</code> and | |
<code>JSONBadgerfishMessageFormatter</code> use Jettision to create an appropriate <code>XMLStreamWriter</code> | |
and then request Axiom to serialize the body element to that <code>XMLStreamWriter</code>. If a databinding | |
is used, then the body element will typically be an <code>OMSourcedElement</code> with an <code>OMDataSource</code> | |
implementation specific to that databinding. <code>OMSourcedElement</code> will delegate the serialization | |
request to the appropriate method defined by <code>OMDataSource</code>. This means that the databinding code | |
directly writes to the <code>XMLStreamWriter</code> instance provided by Jettision, without building an | |
intermediate XML object model.</p> | |
<p>Before doing this, the JSON message formatters actually check if the element is an <code>OMSourcedElement</code> | |
backed by a corresponding JSON <code>OMDataSource</code> implementation. If that is the case, then they will | |
extract the JSON payload and directly write it to the output stream. This allows JSON aware services to | |
bypass the XML to JSON conversion entirely.</p> | |
</subsection> | |
</section> | |
<section name="Tests and Samples"> | |
<subsection name="Integration Test"> | |
<p>The JSON integration test is available under | |
test in the | |
json module of Axis2. It uses the | |
SimpleHTTPServer to deploy the service. A simple echo service is | |
used to return the incoming OMSourcedElement object, which | |
contains the JSONDataSource. There are two test cases for two | |
different conventions and another one test case to send the request | |
in GET.</p> | |
</subsection> | |
<subsection name="Yahoo-JSON Sample"> | |
<p>This sample is available in the | |
samples module of Axis2. It is a | |
client which calls the Yahoo search API using the GET method, with | |
the parameter output=json. The | |
Yahoo search service sends the response as a | |
formatted JSON string with | |
the content type text/javascript. | |
This content type is mapped with the JSONOMBuilder in the | |
axis2.xml. All the results are shown in a GUI. To run the sample, | |
execute the ant script.</p> | |
<p>These two applications provide good examples of using JSON | |
within Axis2. By reviewing these samples, you will be able to | |
better understand Axis2's JSON support implementation.</p> | |
</subsection> | |
<subsection name="Enabling mapped JSON on the ADB quickstart sample"> | |
<p>To illustrate how JSON can be enabled on an existing service deployed in Axis2, | |
we will use the ADB stock quote service sample from the | |
<a href="quickstartguide.html#adb">Quick Start Guide</a>. The code for this sample | |
can be found in the <tt>samples/quickstartadb</tt> folder in the binary distribution.</p> | |
<p>Only a few steps are necessary to enable JSON (using the Mapped convention) on | |
that service:</p> | |
<ol> | |
<li> | |
<p>Configure the JSON message builders and formatters in <tt>conf/axis2.xml</tt>. | |
Add the following element to the <tt>messageFormatters</tt>:</p> | |
<pre><![CDATA[ | |
<messageFormatter contentType="application/json" | |
class="org.apache.axis2.json.JSONMessageFormatter"/> | |
]]></pre> | |
<p>Also add the following element to the <tt>messageBuilders:</tt></p> | |
<pre><![CDATA[ | |
<messageBuilder contentType="application/json" | |
class="org.apache.axis2.json.JSONOMBuilder"/> | |
]]></pre> | |
</li> | |
<li> | |
<p>Edit the <tt>services.xml</tt> for the stock quote service and add the following | |
configuration:</p> | |
<pre><![CDATA[ | |
<parameter name="xmlToJsonNamespaceMap"> | |
<mappings> | |
<mapping xmlNamespace="http://quickstart.samples/xsd" jsonNamespace=""/> | |
</mappings> | |
</parameter> | |
]]></pre> | |
<p>The <tt>services.xml</tt> file can be found under | |
<tt>samples/quickstartadb/resources/META-INF</tt>.</p> | |
</li> | |
<li> | |
<p>Build and deploy the service by executing the ant script in | |
<tt>samples/quickstartadb</tt> and then start the Axis2 server using | |
<tt>bin/axis2server.sh</tt> or <tt>bin/axis2server.bat</tt>.</p> | |
</li> | |
</ol> | |
<p>That's it; the stock quote service can now be invoked using JSON. This can be tested | |
using the well known <a href="http://curl.haxx.se/">curl</a> tool:</p> | |
<pre>curl -H 'Content-Type: application/json' -d '{"getPrice":{"symbol":"IBM"}}' http://localhost:8080/axis2/services/StockQuoteService</pre> | |
<p>This will give the following result:</p> | |
<pre>{"getPriceResponse":{"return":42}}</pre> | |
</subsection> | |
</section> | |
</body> | |
</document> |