blob: 25f4722c6cac63bdbde22283b97c39abcd0361ee [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
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>
<properties>
<title>JSON Component</title>
<author email="gk@apache.org">Georg Kallidis</author>
</properties>
<body>
<section name="Overview">
<p>The intent of this component is to provide a configurable and customized integration of JSON De-/Serializers using <a href="https://github.com/google/gson/">GSON</a>, <a href="http://wiki.fasterxml.com/JacksonHome">Jackson 1 and Jackson 2 APIs</a> into Avalon/Turbine. It is a common pattern with client-side MVC applications to embed data for a base set of objects in a page instead of making a separate AJAX request to load them.
It is written for use in Turbine but it can be used in any container compatible
with Avalon's ECM container. There are three implementations
<ul>
<li>GSONBuilderService and</li>
<li>Jackson2MapperService</li>
<li>JacksonMapperService</li>
</ul>
All Services provide basic serialization and deserialization functions for applications. Additional (optional) Features:
<ul>
<li>Support for <a href="https://github.com/json-path/JsonPath/blob/master/README.md">JsonPath</a> in Gson and Jackson 2</li>
<li>Support for <a href="https://github.com/FasterXML/jackson-datatype-json-org">org.json mapping</a> (as registered module or adapter)</li>
</ul>
</p>
</section>
<section name="JSON Service Component with Jackson 1 / 2 API">
<subsection name="Overview">
<p>
This Service serves as a JSON serializer or deserializer using <a href="https://github.com/FasterXML/jackson">Jackson</a> Version 2 or Version 1.
The JSON Jackson 2 sub module is the most elaborated of the three sub modules. It provides attribute and class filtering and cache cleaning mechanism.
</p>
<p>
It is written for use in Turbine but it can be used in any container compatible
with Avalon's ECM container.
</p>
</subsection>
<subsection name="Role Configuration">
<p>Jackson Version 2.7.x</p>
<source><![CDATA[
<role
name="org.apache.fulcrum.json.JsonService"
shorthand="json"
default-class="org.apache.fulcrum.json.jackson.Jackson2MapperService"/>
]]></source>
<p>Jackson Version 1.9.13</p>
<source><![CDATA[
<role
name="org.apache.fulcrum.json.JsonService"
shorthand="json"
default-class="org.apache.fulcrum.json.jackson.JacksonMapperService"/>
]]></source>
</subsection>
<subsection name="Component Configuration" id="compconf">
<table>
<tr>
<th>Item</th>
<th>Datatype</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
<tr>
<td>annotationInspectors</td>
<td>Complex</td>
<td>[0|1]</td>
<td>
If empty <code>com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector</code> is set as annotation introspector. You could otherwise provide a primary and (optionally) a secondary introspector. Setting the special introspector <code>org.apache.fulcrum.json.jackson.SimpleNameIntrospector</code> yields property and class name based filtering. See the configuration example below and in <a href="#velocity">section Usage in Velocity Template</a>. Features could be set to <code>false</code> or <code>true</code> by setting the attribute <b>value</b> of the sub element feature. The feature attribute <b>type</b> should be the class name of any sub interface of <code>com.fasterxml.jackson.databind.cfg.ConfigFeature</code>. The element content itself defines any feature (enum value) for this feature. Cft. the configuration example below.
</td>
</tr>
<tr>
<td>dateFormat</td>
<td>String</td>
<td>[0|*]</td>
<td>
If set changes the date format. The provided string should be in a format acceptable to the class <code>java.text.SimpleDateFormat.SimpleDateFormat(String)</code>. The default value is
<code>MM/dd/yyyy</code>.
</td>
</tr>
<tr>
<td>defaultTyping</td>
<td>String</td>
<td>[0|*]</td>
<td>
The default is no defaultTyping. Otherwise set it to a Jackson 2 enum value in class <code>com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping</code>.
</td>
</tr>
<tr>
<td>cacheFilters</td>
<td>boolean</td>
<td>[0|*]</td>
<td>
If set to <code>true</code>, caching is enabled. Each filter applied remains valid and is not removed.
This implicits, that you cannot retrieve for the same class/bean different properties in different calls.
Otherwise different serializations per call are possible. You could invalidate (refresh) the cache per class, in most methods providing a Boolean parameter, cft. method parameters named <code>refresh*</code> or <code>clean*</code>. The default value is <code>true</code>.
</td>
</tr>
<tr>
<td>escapeCharsGlobal</td>
<td>boolean</td>
<td>[0|1]</td>
<td>
If set to <code>true</code>, by default ASCII characters &lt;= 32 character code and the four characters '&lt;','&gt;','&amp;','\' (backslash) are escaped using com.fasterxml.jackson.core.io.CharacterEscapes.ESCAPE_STANDARD ('\u'). for details cft. com.fasterxml.jackson.core.io.CharTypes. The default value is <code>false</code>.
</td>
</tr>
<tr>
<td>useJsonPath</td>
<td>boolean</td>
<td>[0|1]</td>
<td>
If set to <code>true</code>, JsonPath is enabled, which allows to apply JsonPath expressions using the integrated jackson provider by default (without setting this property to <code>true</code>, com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider will be used and would need to implement net.minidev.json.writer.JsonReaderI by default - which would fail as net.minidev.json package is excluded from dependencies; you would need to include this transitive dependency manually). The default value is <code>false</code>.
</td>
</tr>
</table>
</subsection>
<subsection name="Component Configuration Example">
<p>Jackson Version 2.x</p>
<source><![CDATA[
<json>
<annotationInspectors>
<primary>org.apache.fulcrum.json.jackson.SimpleNameIntrospector</primary>
<secondary>com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector</secondary>
<features>
<feature value="false" type="com.fasterxml.jackson.databind.SerializationFeature">FAIL_ON_EMPTY_BEANS</feature>
<feature value="false" type="com.fasterxml.jackson.databind.DeserializationFeature">EAGER_DESERIALIZER_FETCH</feature>
<!-- do not fail of only getter is provided -->
<feature value="false" type="com.fasterxml.jackson.databind.DeserializationFeature">FAIL_ON_UNKNOWN_PROPERTIES</feature>
<feature value="false" type="com.fasterxml.jackson.databind.MapperFeature">ALLOW_FINAL_FIELDS_AS_MUTATORS</feature>
<feature value="true" type="com.fasterxml.jackson.core.JsonParser">ALLOW_UNQUOTED_FIELD_NAMES</feature>
<!-- feature value="true">com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT</feature-->
</features>
</annotationInspectors>
<dateFormat>MM/dd/yyyy</dateFormat>
<defaultTyping type="NON_FINAL" key="type"/><!-- or e.g. OBJECT_AND_NON_CONCRETE -->
<!-- cacheFilters>false</cacheFilters -->
<!-- <escapeCharsGlobal>true</escapeCharsGlobal> -->
<useJsonPath>true</useJsonPath>
</json>
]]></source>
<p>Jackson Version 1.9.x</p>
<source><![CDATA[
<json>
<annotationInspectors>
<primary>org.apache.fulcrum.json.jackson.CustomIntrospector</primary>
<secondary>org.codehaus.jackson.xc.JaxbAnnotationIntrospector</secondary>
<features>
<!-- support up to now only serializing features -->
<feature value="false">org.codehaus.jackson.map.SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS</feature>
<!-- feature value="true">org.codehaus.jackson.map.SerializationConfig.Feature.INDENT_OUTPUT</feature-->
</features>
</annotationInspectors>
<dateFormat>MM/dd/yyyy</dateFormat>
<!-- defaultTyping type="OBJECT_AND_NON_CONCRETE" key="type"/-->
<!-- cacheFilters>false</cacheFilters-->
</json>
]]></source>
</subsection>
<subsection name="Usage">
<p>
You get a JSON service from the service like this:
</p>
<source><![CDATA[
JsonService jsonService = (JsonService)TurbineServices
.getInstance().getService(JsonService.ROLE);
]]></source>
</subsection>
<subsection name="Usage in Velocity Template: Serialization Example" id="velocity">
<p>
A lot of client data is nowadays provided by javascript and usage of Model-View-View Model (MVVM) frameworks is very popular. Having the required data in JSON format would be of some help.
Velocity provides the integration of Java objects into templates (HTML). To generate in this context
the data which should be exposed into Javascript you could integrate the provided serialization methods here, in the velocity context. As an example, you could provide it via the Turbine Pull service:
</p>
<source><![CDATA[
public Object getJson(Object src, String className, Boolean refresh, String... props ) {
String result= null;
try
{
Class clazz = Class.forName(className);
result = jsonService.serializeOnlyFilter( src, clazz, refresh, props );
}
catch ( Exception e )
{
log.error(e.getMessage(),e );
}
return result;
}
]]></source>
You could then call the JSON method from this tool in a velocity template like this:
<source><![CDATA[
#set ($json = $!pullTool.getJson($items, "x.y.z.Item", true, "prop1", "prop2", "prop3" ) )
## parse json in javascript ....
]]></source>
What you get is the JSON data populated with all fields you provided (starting with the fourth parameter). Th result format may vary depending on the serialization parameters. The third parameter being true will cache not the result, but the call characteristics i.e. the parameters used for the serialization of the provided class.
<h4>Configuration Requirements</h4>
<p>
Add <code>org.apache.fulcrum.json.jackson.SimpleNameIntrospector</code> to the annotation inspectors as primary or secondary inspector. <strong>[CHANGE in Version 1.1.0]:</strong> SimpleNameIntrospector now extends from <a href="https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java">NopAnnotationIntrospector</a> to cleary divide responsibilities. Default introspector e.g. JacksonAnnotationIntrospector could be provided as a primary/secondary introspector in <a href="#compconf">Component Configuration</a>.
</p>
</subsection>
<subsection name="JSON to Object Deserialization" id="deser">
<p>
This could be done by providing the JSON data as client parameter to a JSON-RPC-Service function (cft. services->JSON-RPC-Service). As an example consider this:
</p>
<source><![CDATA[
// class is registered in screen
public <T> void deSerJsonItem(String src ) {
try
{
Item result = jsonService.deSer( src, Item.class );
// do something with result
}
catch ( Exception e )
{
log.error(e.getMessage(),e );
}
}
}
]]></source>
You could then call the JSON method in a Javascript section like this:
<source><![CDATA[
jsonrpc.myFunctions.deSerJsonItem( JSON.stringify(jsonitem) );
]]></source>
</subsection>
</section>
<section name="JSON Service Component with GSON API">
<section name="Overview">
<p>
This Service serves as a JSON serializer or deserializer using <a href="https://code.google.com/p/google-gson/">GSON</a> Version 2.3.1
The GSON sub module provides some include/exclude filtering strategies.
</p>
<p>
It is written for use in Turbine but it can be used in any container compatible
with Avalon's ECM container.
</p>
</section>
<subsection name="Role Configuration">
<source><![CDATA[
<role
name="org.apache.fulcrum.json.JsonService"
shorthand="json"
default-class="org.apache.fulcrum.json.gson.GSONBuilderService"/>
]]></source>
</subsection>
<subsection name="Component Configuration">
<table>
<tr>
<th>Item</th>
<th>Datatype</th>
<th>Cardinality</th>
<th>Description</th>
</tr>
<tr>
<td>globalAdapters</td>
<td>Complex</td>
<td>[0|1]</td>
<td>
If not set no adapter is set. Otherwise provide for each sub element <code>adapter</code> the class name of the adapter and set the attribute <code>forClass</code> to the class name the adapter should be applied. See the configuration example below.
</td>
</tr>
<tr>
<td>dateFormat</td>
<td>String</td>
<td>[0|*]</td>
<td>
If set changes the date format. Provided string should be in a Format acceptable to the class <code>java.text.SimpleDateFormat.SimpleDateFormat(String)</code>. The default value is
<code>MM/dd/yyyy</code>.
</td>
</tr>
<tr>
<td>useJsonPath</td>
<td>boolean</td>
<td>[0|1]</td>
<td>
If set to <code>true</code>, JsonPath is enabled, which allows to apply JsonPath expressions using the integrated gson provider by default. The default value is <code>false</code>. For more information see jackson2 component configuration property description.
</td>
</tr>
</table>
</subsection>
<subsection name="Component Configuration Example">
<source><![CDATA[
<json>
<dateFormat>MM/dd/yyyy</dateFormat>
<globalAdapters>>
<adapter forClass="x.y.z.Class">a.b.c.d.AdapterForClassXYZ</adapter-->
</globalAdapters>
<useJsonPath>true</useJsonPath>
</json>
]]></source>
</subsection>
<subsection name="Usage">
<p>
You get a JSON service from the service like this:
</p>
<source><![CDATA[
JsonService jsonService = (JsonService)TurbineServices
.getInstance().getService(JsonService.ROLE);
]]></source>
<p>
</p>
</subsection>
</section>
</body>
</document>