blob: 1962da2a07cbc8a74134cb016a2b8b5809e19296 [file] [log] [blame]
<?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
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
<document xmlns=""
<title>New JSON support in Apache Axis2</title>
<h1>New JSON support in Apache Axis2</h1>
<p>Update: Moshi support is now included as an alternative to GSON,
though both are supported and will continue to be. Both libs are very
similar in features though Moshi is widely considered to have better
performance. GSON development has largely ceased. Switching between
Moshi and GSON is a matter of editing the axis2.xml file.
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>This documentation explains how the existing JSON support in Apache Axis2 have been improved with two new
methods named, Native approach and XML stream API based approach. Here it initially explains about the
drawbacks of the old JSON support, how to overcome those drawbacks with the new approaches and, how to use
those approaches to send pure JSON objects without converting it to any XML representations. XML Stream API
based approach addresses the namespace problem with pure JSON support, and provides a good solution for
<section name="Introduction">
<p>Apache Axis2 is an efficient third generation SOAP processing web services engine. JSON (JavaScript
Object Notation) is a lightweight data-interchange format and, an alternative for XML which is easily
human readable and writable as well as it can be parsed and generated easily for machines.</p>
<p>The existing JSON implementation in Apache Axis2/Java supports badgerfish format of the JSON object,
which is an XML representation of JSON object. With this approach, it first completely converts the
badgerfish JSON string to the relevant XML format in the server side and, treats it as a normal SOAP
message. The current JSON implementation also supports Mapped JSON, which is another XML representation
of the JSON object, if we set xmlToJsonNamespaceMap parameter in the services.xml file of the service.
The main problem with Mapped JSON format is, at the client side it is not aware of the namespaces which
is used in server side to validate the request. Therefore with current implementation, it is required
to do modifications to the existing services to use this mapped format support. The current JSON
implementations of Axis2 are slower than the existing SOAP support too. Therefore the existing JSON
support doesn't expose its advantages at all.</p>
<p>However this JSON support can be improved to support pure JSON objects without using any format to
convert it into a XML, as JSON is a light weighted alternative to XML. Thre are two new approaches
have been used with google-gson library, a rich library to convert a JSON string to a Java object
and vice-versa, in order to improve the existing JSON support in Apache Axis2/java.</p>
<p>Gson[1] is a Java library that can be used to convert Java Objects into their JSON representation.
It can be also used to convert a JSON string to an equivalent Java object. Gson can work with arbitrary
Java objects including pre-existing objects for which you do not have the source code.</p>
<p>There are a few open source projects, capable of converting Java objects into JSON. However, most of
them require to place Java annotations in all classes; something that cannot be done if we do not have
access to the source code. Most of them also do not fully support the use of Java Generics. Gson has
considered both of these facts as very important design goals and, the following are some of the
advantages of using Gson to convert JSON into Java objects and vice-versa.</p>
<li>It provides simple toJSON() and fromJSON() methods to convert Java objects into JSON objects and
<li>It allows pre-existing unmodifiable objects to be converted into/from JSON objects.</li>
<li>It has the extensive support of Java Generics.</li>
<li>It allows custom representations for objects.</li>
<li>It supports arbitrarily complex objects (with deep inheritance hierarchies and extensive use of
generic types).</li>
<p>As mentioned above, these two new approaches have been introduced to overcome the above explained
problems in the existing JSON implementation (Badgerfish and Mapped) of Axis2. The first one, the
Native approach, has been implemented with completely pure JSON support throughout the axis2 message
processing process while with the second approach which is XML stream API based approach, an
XMLStreamReader/XMLStreamWriter implementation using google-gson with the help of XMLSchema is being
implemented. The detailed description on the two approaches is given out in the next sections of the
<section name="Native Approach" id="native_approach" >
<p>With this approach you can expose your POJO service to accept pure JSON request other than converting to
any representation or format. You just need to send a valid JSON string request to the service url and,
in the url you should have addressed the operation as well as the service. Because in this scenario Axis2
uses URI based operation dispatcher to dispatch the correct operation. in
<a href="json_gson_user_guide.html#native_approach">here</a> you can
find the complete user guide for this native approach.</p>
<p>The Native approach is being implemented to use pure JSON throughout the axis2 message processing
process. In Axis2 as the content-type header is used to specify the type of data in the message body,
and depending on the content type, the wire format varies. Therefore, we need to have a mechanism to
format the message depending on content type. We know that any kind of message is represented in Axis2
using AXIOM, and when we serialize the message, it needs to be formatted based on content type.
MessageFormatters exist to do that job for us. We can specify MessageFormatters along with the content
type in axis2.xml. On the other hand, a message coming into Axis2 may or may not be XML, but for it to
go through Axis2, an AXIOM element needs to be created. As a result, MessageBuilders are employed to
construct the message depending on the content type.</p>
<p>After building the message it gets pass through AXIS2 execution chain to execute the message. If the
message has gone through the execution chain without having any problem, then the engine will hand over
the message to the message receiver in order to do the business logic invocation. After this, it is up
to the message receiver to invoke the service and send the response. So according to the Axis2
architecture to accomplish this approach it is required to implement three main components, a
MessageBuilder, a MessageReceiver and a MessageFormatter, where in this Native implementation those are
JSONBuilder, JSONRPCMessageReceiver and JSONFomatter, to handle the pure JSON request and invoke the
service and return a completely pure JSON string as a response. In the builder, it creates and returns
a dummy SOAP envelop to process over execution chain, while storing input json stream and a boolean
flag IS_JSON_STREAM as a property of input MessageContext. The next one is JSONRPCMessageReceiver which
is an extended subclass of RPCMessageReceiver. In here it checks IS_JSON_STREAM property value, if it is
'true' then it processes InputStream stored in input MessageContext, using JsonReader in google-gson API.
Then it invokes request service operation by Gson in google-gson API which uses Java reflection to invoke
the service. To write the response to wire, it stores the returned object and return java bean type, as
properties of output MessageContext. If IS_JSON_STREAM is 'false' or null then it is handed over to its
super class RPCMessageReceiver, in order to handle the request. This means, using JSONRPCMessageReceiver
as the message receiver of your service you can send pure JSON messages as well as SOAP requests too.
There is a JSONRPCInOnly-MessageReceiver which extends RPCInOnlyMessageReceiver class, to handle In-Only
JSON requests too. In JSONformatter it gets return object and the java bean type, and writes response as
a pure JSON string to the wire using JsonWriter in google-gson API. The native approach doesn’t support
namespaces. If you need namespace support with JSON then go through XML Stream API based approach.</p>
<section name="XML Stream API Base Approach" id="xml_stream_api_base_approach" >
<p> As you can see the native approach can only be used with POJO services but if you need to expose your
services which is generated by using ADB or xmlbeans databinding then you need to use this XML Stream
API based approach. With this approach you can send pure JSON requests to the relevant services.
Similar to the native approach you need to add operation name after the service name to use uri based
operation dispatch to dispatch the request operation.
<a href="json_gson_user_guide.html#xml_stream_api_base_approach">Here</a> you can see the user guide
for this XML Stream API based approach.</p>
<p>As mentioned in Native approach, Apache Axis2 uses AXIOM to process XML. If it can be implement a way to
represent JSON stream as an AXIOM object which provides relevant XML infoset while processing JSON
stream on fly, that would be make JSON, in line with Axis2 architecture and will support the services
which have written on top of xmlstream API too.</p>
<p>There are a few libraries like jettison , Json-lib etc. which already provide this
XMLStreaReader/XMLStreamWriter interfaces for JSON. There is no issue in converting JSON to XML, as we
can use jettison for that, but when it comes to XML to JSON there is a problem. How could we identify
the XML element which represent JSON array type? Yes we can identify it if there is two or more
consecutive XML elements as jettison does, but what happen if the expected JSON array type has only one
value? Then there is only one XML element. If we use Json-lib then xml element should have an attribute
name called "class" value to identify the type of JSON. As you can see this is not a standard way and
we cannot use this with Axis2 as well. Hence we can't use above libraries to convert XML to JSON
accurately without distort expected JSON string even it has one value JSON array type.</p>
<p>Therefore with this new improvement Axis2 have it's own way to handle incoming JSON requests and
outgoing JSON responses. It uses GsonXMLStreamReader and GsonXMLStreamWriter which are implementations
of XMLStreamReader/XMLStreamWriter, to handle this requests and responses. To identify expected request
OMElement structure and namespace uri, it uses XmlSchema of the request and response operations. With
the XmlSchema it can provide accurate XML infoset of the JSON message. To get the relevant XMLSchema
for the operation, it uses element qname of the message. At the MessageBuilder level Axis2 doesn't know
the element qname hence it can't get the XmlSchema of the operation. To solve this issue Axis2 uses a
new handler call JSONMessageHandler, which executes after the RequestURIOperationDispatcher handler.
In the MessageBuilder it creates GsonXMLStreamReader parsing JsonReader instance which is created using
inputStream and stores it in input MessageContext as a message property and returns a default SOAP
envelop. Inside the JSONMessageHandler it checks for this GsonXMLStreamReader property, if it is not
null and messageReceiver of the operation is not an instance of JSONRPCMessageReceiver, it gets the
element qname and relevant XMLSchema list from the input MessageContext and pass it to
GsonXMLStreamReader. After that it creates StAXOMBuilder passing this GsonXMLStreamReader as the
XMLStreamReader and get the document element. Finally set this document element as child of default
SOAP body. If Axis2 going to process XMLSchema for every request this would be a performance issue.
To solve this, Axis2 uses an intermediate representation(XmlNode) of operation XmlSchema list and store
it inside the ConfigurationContext with element qname as a property to use it for a next request
which will come to the same operation. Hence it only processes XmlSchema only once for each operation.</p>
<p>Same thing happens in the JsonFormatter, as it uses GsonXMLStreamWriter to write response to wire and
uses intermediate representation to identify the structure of outgoing OMElement. As we know the
structure here we can clearly identify expected JSON response. Even expected JSON string have a JSON
Array type which has only one value. </p>
<p>In addition, XML Stream API based approach supports namespace uri where it get namespaces from the
operation XMLSchema and provides it when it is asked. </p>