-
Notifications
You must be signed in to change notification settings - Fork 245
JSON serialization
(released with 2.29)
The Json codec provides the possibility to do full fledged Object Graph serialization based on JSon format. Other than existing solutions (e.g. Jackson Databind or Boon) it is fully precise (Object Graph is identical after deserialization) and supports/restores cyclic and interlinked object graphs same as serialization. It uses Jackson-Core under the hood to parse/generate Json, so its FST's serialization implementation using a Jackson-back end instead of byte arrays for in/output.
Ofc there is a cost for maintaining full type information, so performance is reasonable, however for simple non cyclic datastructures, non-generic Json libs are faster (at least the performance leaders jackson/boon). However once data gets more complex, frequently manual translation Application Data <=> Json Intermediate Message representation has to be done (nobody adds this to benchmarks :-) ).
- cross platform/language interoperability.
- productivity. Leverage existing interfaces by just switching the codec from native serialization to Json.
- readability/historical safety. Can be read+processed by any tool. Original classes are not required.
- generalization+abstraction. E.g. Kontraktor 3.0 provides pluggable networking (TCP, WebSockets, Http) in combination with different flavors of data encoding without the need to change anything in application code.
- Serializes
Serializable
andExternalizable
object graphs to Json keeping precise type information same as JDK and FST Serialization - Support for cyclic and linked data structures same as serialization
- Flat mode (ignore references in object graph) supported
- Reuse existing serialization code to support Json.
- embedded type information produces somewhat ugly Json output (btw: machines don't mind ;) ). Additionally there is a performance cost for additional type information tags compared to handpicked Json encoding.
- classes using old school JDK serialization implementations (readObject/writeObject/readReplace..) are not supported, one needs to register FST-Serializers for those. Fortunately fst comes with a broad range of predefined+preconfigured Serializers (Collections, frequently used Data Types), so for most cases it will work out of the box same as regular FST Serialization.
static class SimpleClass implements Serializable {
String name = "You";
double aDouble = 13.3456;
int anInt;
}
public static void main(String[] args) throws UnsupportedEncodingException {
FSTConfiguration conf = FSTConfiguration.createJsonConfiguration();
Object p = new SimpleClass();
byte[] bytes = conf.asByteArray(p);
System.out.println(new String(bytes,"UTF-8"));
Object deser = conf.asObject(bytes);
}
yields:
{
"type": "ser.Play$SimpleClass",
"obj": {
"aDouble": 13.3456,
"anInt": 0,
"name": "You"
}
}
each regular serialized object results in {"type": ..java type.. obj:{..data key/val..}}
. By preregistering classes, class names can be shortened. HashMap, ArrayList, Object arrays are already preregistered (named 'list', 'map' and 'array').
Native primitive arrays (+ class registration conf.registerCrossPlatformClassMappingUseSimpleName(SimpleClass.class);
)
public static class SimpleClass implements Serializable {
String name = "You";
double aDouble = 13.3456;
int anInt;
int integers[] = { 1,2,3,4,5 };
short shorts[] = { 1,2,3,4,5 };
}
yields (notice the type tagging of short array. All primitive arrays except int arrays are type tagged, required to handle e.g. Object o = new short[] { 1,23 };
):
{
"type": "SimpleClass",
"obj": {
"aDouble": 13.3456,
"anInt": 0,
"name": "You",
"integers": [ 1, 2, 3, 4, 5 ],
"shorts": [ "short", 1, 2, 3, 4, 5 ]
}
}
Externalizable / classes with Custom Serializer registered use a slightly different object notation: {"type": ..java type.. obj:[..data key/val..]}
. The 'obj' value is an array containing the output of an externalize implementation or custom serializer write method.
public static class SampleClass implements Serializable {
List myList = new ArrayList();
Map<Integer,String> myMap = new HashMap<>();
{
myMap.put(1,"Some String");
myList.add(1); myList.add(2); myList.add("Hello");// myList.add(new Date());
}
}
yields:
{
"type": "ser.Play$SampleClass",
"obj": {
"myList": {
"type": "list",
"obj": [ 3, //number_of_list_elements
1, 2, "Hello"
]
},
"myMap": {
"type": "map",
"obj": [
1, //number_of_hashmap_entries
1, "Some String"
]
}
}
}
As the FSTCollection Serializer first writes the length of a collection, then the elements of the collection, you see this reflected in the 'obj:[]' json array. Note that "HashMap" and "ArrayList" are preregistered to be shortened as "map" and "list". This also opens opportunities to shorten the Json footprint of frequently serialized classes by registering a custom serializer (no field names written).
non-primitive and multidimensional arrays
public static class SampleClass implements Serializable {
int iii[][][] = new int[][][] { { {1,2,3}, {4,5,6} }, { {7,8,9}, {10,11,12} } };
Object objArr[][] = { { "A", "B" }, null, { null, "C", "D" } };
}
yields (again for sequences, first element is size of list)
{
"type": "SampleClass",
"obj": {
"objArr": {
"seqType": "[[Ljava.lang.Object;",
"seq": [
3,
{
"seqType": "array",
"seq": [ 2, "A", "B" ]
},
null,
{
"seqType": "array",
"seq": [ 3, null, "C", "D" ]
}
]
},
"iii": {
"seqType": "[[[I",
"seq": [
2,
{
"seqType": "[[I",
"seq": [ 2, [ 1, 2, 3 ], [ 4, 5, 6 ] ]
},
{
"seqType": "[[I",
"seq": [ 2, [ 7, 8, 9 ], [ 10, 11, 12 ] ]
}
]
}
}
}
registering classnames (there are also methods to register arbitrary names)
conf.registerCrossPlatformClassMappingUseSimpleName(
SampleClass.class,
Object[].class,
Object[][].class,
int[][].class,
int[][][].class
);
yields ('array' is preregistered for 'Object[]')
{
"type": "SampleClass",
"obj": {
"objArr": {
"seqType": "Object[][]",
"seq": [
3,
{
"seqType": "array",
"seq": [ 2, "A", "B" ]
},
null,
{
"seqType": "array",
"seq": [ 3, null, "C", "D" ]
}
]
},
"iii": {
"seqType": "int[][][]",
"seq": [
2,
{
"seqType": "int[][]",
"seq": [ 2, [ 1, 2, 3 ], [ 4, 5, 6 ] ]
},
{
"seqType": "int[][]",
"seq": [ 2, [ 7, 8, 9 ], [ 10, 11, 12 ] ]
}
]
}
}
}
references
public static class SampleClass implements Serializable {
String a = "bla bla bla bla bla bla bla bla bla bla bla bla bla ";
Object b = a;
}
{
"type": "SampleClass",
"obj": {
"b": "bla bla bla bla bla bla bla bla bla bla bla bla bla ",
"a": {
"ref": 32
}
}
}
Note the ref value denotes the position of the object in the non-pretty printed Json string. That's why references don't work for pretty printed Json. References can be turned off at FSTConfiguration, if sharing is disabled, there is also no detection for cyclic references anymore.
pretty printing
interop/postprocessing in JavaScript