Overview

The intent of this component is to provide a configurable and customized integration of JSON De-/Serializers using GSON, Jackson 1 and Jackson 2 APIs 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

  • GSONBuilderService and
  • Jackson2MapperService
  • JacksonMapperService
All Services provide basic serialization and deserialization functions for applications. Additional (optional) Features:

JSON Service Component with Jackson 1 / 2 API

Overview

This Service serves as a JSON serializer or deserializer using Jackson 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.

It is written for use in Turbine but it can be used in any container compatible with Avalon's ECM container.

Role Configuration

Jackson Version 2.7.x

  <role
        name="org.apache.fulcrum.json.JsonService"
        shorthand="json"
        default-class="org.apache.fulcrum.json.jackson.Jackson2MapperService"/>

Jackson Version 1.9.13

  <role
        name="org.apache.fulcrum.json.JsonService"
        shorthand="json"
        default-class="org.apache.fulcrum.json.jackson.JacksonMapperService"/>

Component Configuration

Item Datatype Cardinality Description
annotationInspectors Complex [0|1] If empty com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector is set as annotation introspector. You could otherwise provide a primary and (optionally) a secondary introspector. Setting the special introspector org.apache.fulcrum.json.jackson.SimpleNameIntrospector yields property and class name based filtering. See the configuration example below and in section Usage in Velocity Template. Features could be set to false or true by setting the attribute value of the sub element feature. The feature attribute type should be the class name of any sub interface of com.fasterxml.jackson.databind.cfg.ConfigFeature. The element content itself defines any feature (enum value) for this feature. Cft. the configuration example below.
dateFormat String [0|*] If set changes the date format. The provided string should be in a format acceptable to the class java.text.SimpleDateFormat.SimpleDateFormat(String). The default value is MM/dd/yyyy.
defaultTyping String [0|*] The default is no defaultTyping. Otherwise set it to a Jackson 2 enum value in class com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.
cacheFilters boolean [0|*] If set to true, 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 refresh* or clean*. The default value is true.
escapeCharsGlobal boolean [0|1] If set to true, by default ASCII characters <= 32 character code and the four characters '<','>','&','\' (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 false.
useJsonPath boolean [0|1] If set to true, JsonPath is enabled, which allows to apply JsonPath expressions using the integrated jackson provider by default (without setting this property to true, 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 false.

Component Configuration Example

Jackson Version 2.x

<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>

Jackson Version 1.9.x

<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>

Usage

You get a JSON service from the service like this:

JsonService jsonService = (JsonService)TurbineServices
        .getInstance().getService(JsonService.ROLE);

Usage in Velocity Template: Serialization Example

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:

    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;        
    }
You could then call the JSON method from this tool in a velocity template like this:
    #set ($json =   $!pullTool.getJson($items, "x.y.z.Item", true, "prop1", "prop2", "prop3" ) )
    
    ## parse json in javascript ....
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.

Configuration Requirements

Add org.apache.fulcrum.json.jackson.SimpleNameIntrospector to the annotation inspectors as primary or secondary inspector. [CHANGE in Version 1.1.0]: SimpleNameIntrospector now extends from NopAnnotationIntrospector to cleary divide responsibilities. Default introspector e.g. JacksonAnnotationIntrospector could be provided as a primary/secondary introspector in Component Configuration.

JSON to Object Deserialization

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:

      // 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 );
            }       
        }       
      }
You could then call the JSON method in a Javascript section like this:
       jsonrpc.myFunctions.deSerJsonItem( JSON.stringify(jsonitem) );

JSON Service Component with GSON API

Overview

This Service serves as a JSON serializer or deserializer using GSON Version 2.3.1 The GSON sub module provides some include/exclude filtering strategies.

It is written for use in Turbine but it can be used in any container compatible with Avalon's ECM container.

Role Configuration

    <role
         name="org.apache.fulcrum.json.JsonService"
        shorthand="json"
        default-class="org.apache.fulcrum.json.gson.GSONBuilderService"/>
      

Component Configuration

Item Datatype Cardinality Description
globalAdapters Complex [0|1] If not set no adapter is set. Otherwise provide for each sub element adapter the class name of the adapter and set the attribute forClass to the class name the adapter should be applied. See the configuration example below.
dateFormat String [0|*] If set changes the date format. Provided string should be in a Format acceptable to the class java.text.SimpleDateFormat.SimpleDateFormat(String). The default value is MM/dd/yyyy.
useJsonPath boolean [0|1] If set to true, JsonPath is enabled, which allows to apply JsonPath expressions using the integrated gson provider by default. The default value is false. For more information see jackson2 component configuration property description.

Component Configuration Example

  <json>
    <dateFormat>MM/dd/yyyy</dateFormat>
    <globalAdapters>>
      <adapter forClass="x.y.z.Class">a.b.c.d.AdapterForClassXYZ</adapter-->
    </globalAdapters>
    <useJsonPath>true</useJsonPath>
  </json>

Usage

You get a JSON service from the service like this:

JsonService jsonService = (JsonService)TurbineServices
        .getInstance().getService(JsonService.ROLE);