StreamScape JSON Serializier

StreamScape JSON serializer is based on Jackson serializer, most faster and widely used JSON serializer implemented in Java. Jackson serializer is wrapped to our adapter and extended with additional customization properties. 

This document describes JSON serializer customization properties and provides comprehensive examples. 

Why do we need to customize JSON serializer?

Default JSON serializer doesn’t add any type information to the produced JSON. Without any type information consumer of produced JSON would not be able to deserialize this JSON if he doesn’t know the whole structure of serialized object. Type meta information is intended to provide knowledge about serialized objects for those who will process produced JSON.

On another hand when JSON consumer is familiar with received object and its structure he doesn’t need any type information inside JSON. 

Another example, object field can be assigned with polymorphic values and consumer only knows type of root object. In that case to correctly process produced JSON by consumer it should contain type information for polymorphic values.

We cannot add all type metadata to the JSON every time, because in that case produced JSON will contain a lot of metadata that will be redundant for most of consumers.

So that is why meta type info customization parameters are added JSON serializer factory.

Customization properties are divided into two groups. First group contains properties that configure type meta information. Another group contains properties that configure null valued fields and formatting.



 
Type meta information properties

Notation Type

Notation type property defines the place where type information is located. The possible values are ‘type’ and ‘top_lement’.

In case of `type’ type info is added as additional field with name ‘@type’ and value of object type.
In case of `top_element’ notated object is wrapped to the object with name of object type.

  Java Object	Top Element Notation	Type Notation
class A
{
 String stringField;
 int intField;
 Object nullField;
}

A a = new A();
a.stringField="Hello";
a.intField = 1;
a.nullField = null;
 	{
  "A" :
  {
    "stringField":"Hello",
    "intField" : 1,
    "nullField" : null
  }
}	{
  "@type" : “A”
  "stringField" : "Hello",
  "intField" : 1,
  "nullField" : null
}

Default serializer configured to `top_element` notation.

Notation Level

Notation level defines an amount of type meta information.  It defines which objects should be notated with type information. Levels can be joined together in any combinations. Some levels override another levels. But one level value cannot be decreased by another level.

NONE

No type info for any object should be added. Any other level joined with this level overrides this level.

 
ROOT_ELEMENT

Type info should be added for root complex object. Cannot be overridden.

  Java Object	Top Element Notation	Type Notation
class A
{
 String stringField;
 int intField;
 Object nullField;
}

A a = new A();
a.stringField="Hello";
a.intField = 1;
a.nullField = null;
 	ROOT_ELEMENT
	
{
  "A" :
  {
    "stringField":"Hello",
    "intField" : 1
  }
}	{
  "@type" : “A”
  "stringField" : "Hello",
  "intField" : 1,
  “nullField” : null
 }

	
None
	{
  "stringField" : "Hello",
  "intField" : 1,
  “nullField” : null
}


ROOT_ENUM

Type info should be added for root enum. Cannot be overridden.

  Java Object	Top Element Notation	Type Notation
enum E
{
  VALUE1,
  VALUE2
}

E e = E.VALUE1;
	ROOT_ENUM
	
{
  "E" :
  {
    "value":"VALUE1"
  }
}	{
  "@type" : “E”
  "value" : "VALUE1"
}

	
None
	“VALUE1”


COMPLEX_OBJECTS

Type info should be added for any complex object (excluding root object, maps and collections). Cannot be overridden.

  Java Object	Top Element Notation	Type Notation
class B
{
  A a1;
  Object a2;
}

B b = new B();

A a1 = new A();
A1.stringField="s1";
A1.intField = 1;

A a2 = new A();
A2.stringField="s2";
A2.intField = 2;

b1.a1 = a1;
b.a2 = a2;
	COMPLEX_OBJECTS
	
{
  “a1” : {
    “A” :
    {
      “stringField” : “s1”,
      “intField” : 1,
      “nulField” : null
    },
  },
  “a2” : {
    “A” :
    {
      “stringField” : “s2”,
      “intField” : 2,
      “nulField” : null
    }
  }
}	{
  “a1” :
  {
    “@type” : “A”,
    “stringField” : “s1”,
    “intField” : 1,
    “nulField” : null
  },
  “a2” :
  {
    “@type” : “A”,
    “stringField” : “s2”,
    “intField” : 2,
    “nulField” : null
  }
}
	
None
	{
  “a1” :
  {
    “stringField” : “s1”,
    “intField” : 1,
    “nulField” : null
  },
  “a2” :
  {
    “stringField” : “s2”,
    “intField” : 2,
    “nulField” : null
  }
}


POLYMORPHIC_OBJECTS

Type info should be added for any polymorphic object (excluding root object, maps and collections). Can be overridden by COMPLEX_OBJECT level.

  Java Object	Top Element Notation	Type Notation
class B
{
  A a1;
  Object a2;
}

B b = new B();

A a1 = new A();
A1.stringField="s1";
A1.intField = 1;

A a2 = new A();
A2.stringField="s2";
A2.intField = 2;

b1.a1 = a1;
b.a2 = a2;
	POLYMORPHIC_OBJECTS
	
{
  “a1” : 
  {
      “stringField” : “s1”,
      “intField” : 1,
      “nulField” : null
  },
  “a2” : {
    “A” :
    {
      “stringField” : “s2”,
      “intField” : 2,
      “nulField” : null
    }
  }
}	{
  “a1” :
  {
    “stringField” : “s1”,
    “intField” : 1,
    “nulField” : null
  },
  “a2” :
  {
    “@type” : “A”,
    “stringField” : “s2”,
    “intField” : 2,
    “nulField” : null
  }
}
	
None
	{
  “a1” :
  {
    “stringField” : “s1”,
    “intField” : 1,
    “nulField” : null
  },
  “a2” :
  {
    “stringField” : “s2”,
    “intField” : 2,
    “nulField” : null
  }
}


POLYMORPHIC_MAPS_AND_COLLECTIONS

Type info should be added for any polymorphic map. Cannot be overridden.


  Java Object	Top Element Notation	Type Notation
class C
{
  HashMap<String,String> map1;
  Map<String,String> map2;
}

C c = new C();

c.map1=new HashMap<>
();
c.map1.put(“k1”, “v1”);
c.map1.put(“k2”, “v2”);

c.map1=new HashMap<>
();
c.map1.put(“k1”, “v1”);
c.map1.put(“k2”, “v2”);
	POLYMORPHIC_MAPS_AND_COLLECTIONS
	
{
  “map1” : 
  {
    “k1” : “s1”,
    “k2” : “s2”
  },
  “map2” : {
    “map” :
    {
      “k3” : “s3”,
      “k4” : “s4”
    }
  }
}	{
  “map1” : 
  {
    “k1” : “s1”,
    “k2” : “s2”
  },
  “map2” : {
    “@type” : “map”,
    “k3” : “s3”,
    “k4” : “s4”
  }
}
	
None
	{
  “map1” : 
  {
    “k1” : “s1”,
    “k2” : “s2”
  },
  “map2” : 
  {
    “k3” : “s3”,
    “k4” : “s4”
  }
}



PRIMITIVE_OBJECTS

Type info should be added for any primitive object.


  Java Object	Top Element Notation	Type Notation
class D
{
  Integer iField1 = 1;
  Long lField2 = 2;
  Object oField3 = 3.0f;
}

D d = new D();	PRIMITIVE_OBJECTS
	
{
  “iField1” : {
    “int” : {
      “value” : 1
    }
  },
  “lField2” : {
    “long” : {
      “value” : 2
    }
  },
  “oField3” : {
    “float” : {
      “value” : 3.0
    }
  }
}	
{
  “iField1” : {
     “@type“ : “int”,
      “value” : 1
  },
  “lField2” : {
     “@type“ : “long”,
     “value” : 2
  },
  “oField3” : {
     “@type“ : “float”,
     “value” : 3.0
  }
}
	
None
	
{
  “iField1” : 1,
  “lField2” : 2,
  “oField3” : 3.0
}


POLYMORPHIC_PRIMITIVE_OBJECTS

Type info should be added for any polymorphic primitive object. Can be overridden by PRIMITIVE_OBJECTS level.

  Java Object	Top Element Notation	Type Notation
class D
{
  Integer iField1 = 1;
  Long lField2 = 2;
  Object oField3 = 3.0f;
}

D d = new D();	POLYMORPHIC_PRIMITIVE_OBJECTS
	
{
  “iField1” : 1,
  “lField2” : 2,
  “oField3” : {
    “float” : {
      “value” : 3.0
    }
  }
}	
{
  “iField1” : 1,
  “lField2” : 2,
  “oField3” : {
     “@type“ : “float”,
     “value” : 3.0
  }
}
	
None
	
{
  “iField1” : 1,
  “lField2” : 2,
  “oField3” : 3.0
}

ENUMS

Type info should be added for any enum (excluding root enum). Cannot be overridden.

  Java Object	Top Element Notation	Type Notation
class F
{
  E e1 = E.VALUE1;
}

F f = new f();	POLYMORPHIC_PRIMITIVE_OBJECTS
	
{
 “e1” : {
   “E” : {
       “value” : “VALUE1”
   }
 }
}
	
{
 “e1” : {
   “@type” : “E”,
   “value” : “VALUE1”
 }
}

	
None
	
{
 “e1” : “VALUE1”
}



OLD_FABRIC_STYLE_DATE_AND_SQLTIMESTAMP

Old style date and SQL timestamp serializers.
Used by JS fabric api only.

  Java Object	Top Element Notation	Type Notation

class J
{
 java.util.Date date;
 java.sql.Date  sqldate;
 java.sql.Time  sqltime;
 java.sql.Timestamp sqltimestamp;
}

J j = new J();
j.date = new java.util.Date(System.currentTimeMillis());
j.sqldate = new java.sql.Date(System.currentTimeMillis());
j.sqltime = new java.sql.Time(System.currentTimeMillis());
j.sqltimestamp = new java.sql.Timestamp(System.currentTimeMillis());
	POLYMORPHIC_PRIMITIVE_OBJECTS
	
{
"date" : "Mar 2, 2017 5:36:26 PM",
"sqldate" : "2017-03-02",
"sqltime" : "17:36:26",
"sqltimestamp" : "2017-03-02 17:36:26.281",
}
	
{
  "date" : {
    "@type" : "date",
    "value" : "Mar 2, 2017 5:36:26 PM"
  },
  "sqldate" : "2017-03-02",
  "sqltime" : "17:36:26",
  "sqltimestamp" : {
    "@type" : "SqlTimestamp",
    "millis" : 1488465386281
  },
}

	
None
	
{
  "dateobject" : 1488465386411,
  "sqldateobject" : "2017-03-02",
  "sqltimeobject" : "17:36:26",
  "sqltimestampobject" : 1488465386411
}


Another properties

Skip Null Values

If skip null values property is enabled fields with null values will be omitted from produced JSON. 

  Java Object	Top Element Notation	Type Notation
class A
{
 String stringField;
 int intField;
 Object nullField;
}

A a = new A();
a.stringField="Hello";
a.intField = 1;
a.nullField = null;
 	Skip null values
	
{
  "A" :
  {
    "stringField":"Hello",
    "intField" : 1
  }
}	{
  "@type" : “A”
  "stringField" : "Hello",
  "intField" : 1
}

	
Don’t skip null values
	{
  "A" :
  {
    "stringField":"Hello",
    "intField" : 1,
    “nullFiled” : null
  }
}	{
  "@type" : “A”
  "stringField" : "Hello",
  "intField" : 1,
  “nullFiled” : null
}


Disabled by default.

Pretty Print

Defines how produced JSON should look like, one string text or text with new lines after field values and indents.

Disabled by default.

Maps serialization

There is a difference in map serialization when key is string and when key is not string.
If key is not string then map entries are wrapped in “entries”. 

Sample
Consider java class

class G
{
  Map<String, String> mapStringToString;
  Map<String, B> mapStringToB;

  Map<A, B> mapAToB;
}

G g = new G();

 g.mapStringToString = new HashMap<>()
  g.mapStringToString.put(“k1”, “v1”);
  g.mapStringToString.put(“k2”, “v2”);

 g.mapStringToB = new HashMap<>()
  g.mapStringToB.put(“k1”, new B(1));
  g.mapStringToB.put(“k2”, new B(2));

 g.mapAToB = new HashMap<>()
  g.mapAToB.put(new A(3), new B(3));
  g.mapAToB.put(new A(4), new B(4));

JSON serialization

{
  “mapStringToString”:
  {
    “k1” : “v1”,
    “k2” : “v2”
  },
  “mapStringToB”:
  {
    “k1” : {
       “field1” : 1
    },
    “k2” : {
       “field1” : 2
    },
  },
  “mapAToB”:
  {
    “entries” : [
      {
	 “key” : {
          “intField” : 3
        },
        “value” : {
           “field1” : 3
    	 },
      },
      {
	 “key” : {
          “intField” : 4
        },
        “value” : {
           “field1” : 5
    	 },
     }
    ]
  }
}


Date and time serialization

There are 4 date-time types exists
-	java.util.Date
-	java.sql.Date
-	java.sql.Time
-	java.sql.Timestamp

java.util.Date and java.sql.Timestamps are serialized as long value of milliseconds since 1970-01-01 in UTC timezone. 

java.sql.Time and java.sql.Date are serialized as strings in current timezone.

Sample Java class

class J
{
 java.util.Date date;
 java.sql.Date  sqldate;
 java.sql.Time  sqltime;
 java.sql.Timestamp sqltimestamp;
}

J j = new J();
j.date = new java.util.Date(System.currentTimeMillis());
j.sqldate = new java.sql.Date(System.currentTimeMillis());
j.sqltime = new java.sql.Time(System.currentTimeMillis());
j.sqltimestamp = new java.sql.Timestamp(System.currentTimeMillis());

JSON:
{
  "dateobject" : 1488465386411,
  "sqldateobject" : "2017-03-02",
  "sqltimeobject" : "17:36:26",
  "sqltimestampobject" : 1488465386411
}

JSON serializer customization in REST calls

REST calls allows specify request/response formants query parameters. If request or response format is JSON its serializer can be customized with the following properties:
-	type – defines notation type
-	level – defines notation level
-	skipnulls – skip nulls or not
-	prettyprint – pretty print or not

Samples
	json;notation=type
	json;level=top_element;level=root_element,complex_objects
	json;notation=type;level=top_element;complex_objects; prettyprint=true;skipnulls=true

Default serializer is top element notation with level=root_element,root_enum,polymorphic_object,polymorphic_maps_and_collections

JSON serializer creation and customization in Java

// returns default JSON serializer instance, configured to
//  - notation: top element notation
//  - level: root objects, root enums, polymorphic objects, polymorphic map and collection
//  - no pretty print
//  - don't skip nulls
RuntimeContext.getInstance().getJSONSerializer();

// returns JSON serializer factory bound to current runtime
// factort stores all serializers created using this factory
JSONSerializerFactory factory = RuntimeContext.getInstance().getJSONSerializerFactory();

// creates default serialize with specified name
JSONSerializer serializer = factory.createSerializer("MyDefaultSerializer");

// create pretty print serializer
JSONSerializer prettyPrintSerialzier = serializer.withPrettyPrint(true);

// create customized serializer using lazy builder
// lazy builder at first checks if serializer with specified name exist and
// if doesn't exist creates new
factory.createSerializerDefaultBuilderLazy("MyCustomizedSerializer")
   .apply(builder -> {    
        builder  // set serializer proprties
           .setJsonNotation(JsonNotation.TOP_ELEMENT)
           .setJsonNotationLevel(JsonNotationLevel.ROOT_ELEMENT, JsonNotationLevel.COMPLEX_OBJECTS)
           .setPrettyPrint(true)
           .setSkipNulls(true);
   })
   .getOrBuild(); // get serializer with specified name or build new one

