Skip to content

Latest commit

 

History

History
161 lines (114 loc) · 8.37 KB

jackson.md

File metadata and controls

161 lines (114 loc) · 8.37 KB

Serializing Eclipse Collections with Jackson

Unfortunately, with new collection types comes incompatibility with normal serialization frameworks. Jackson, arguably the most popular JSON serialization framework for Java, is unable to deserialize Eclipse Collections types out-of-the-box. For this purpose, there is now a Jackson module supporting most Eclipse Collections types directly (including primitive collections).

In this article we will cover the basics of Eclipse Collections, Jackson and that module.

Eclipse Collections

Eclipse Collections replaces many of the familiar container types from the standard library with new and improved versions. For this article, we will look at three variants of the List type:

import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.list.primitive.IntList;
import org.eclipse.collections.impl.factory.primitive.IntLists;

MutableList<String> mutable = Lists.mutable.of("foo", "bar");
ImmutableList<String> immutable = Lists.immutable.of("foo", "bar");
IntList intList = IntLists.immutable.of(1, 2, 3);

MutableList behaves a lot like java.util.List, but offers additional stream-like API we will not go into detail about here. Every MutableList also implements java.util.List, so you can pass any MutableList to APIs that still use the standard Java collections.

ImmutableList does not extend java.util.List and - as its name suggests - does not offer any methods for mutation such as add or remove. Instead you can create copies of these collections with elements added or removed. If you wish to use these collections with traditional java.util.List APIs you can use the castToList() method on ImmutableList, which will return an instance of java.util.List that will throw exceptions on mutation operations like remove.

IntList is a special type of list that offers lightweight APIs for working with primitives. Similar collection types exist for all other primitives. The problem with "normal" collections and primitives is that using a List<Integer> or similar type will always carry additional overhead both in terms of more memory use and more CPU time due to indirection and additional garbage collection load. Because of this, for performance-critical operations, a dedicated IntList type is superior.

Eclipse Collections is hardly the first library to provide primitive collections but combined with its handy API it makes for arguably the best library of its kind. Using Eclipse primitive collections you can have the performance of primitive arrays with the ease-of-use of the Eclipse Collections API.

For all the primitive collection types there also exist mutable and immutable versions (MutableIntList, ImmutableIntList). They behave much the same as their object-based counterparts so we will not cover them here.

Eclipse Collections is split into two parts: The API and the implementation of that API. Since the implementation depends on the API, we will only have to add that as a dependency to our Maven project. See the latest release for the details on the Maven or Gradle coordinates.

Jackson

Jackson is a library for transforming your data structures from and to JSON and other serialized forms. According to mvnrepository.com it is the most popular framework for this purpose.

Jackson's architecture consists of three parts: A core that is responsible for writing streams of data (typically JSON), databind which is responsible for the basic object serialization API, and various Jackson modules that each handle serialization of some special types. Adding Jackson to our project is simple by using the Maven or Gradle coordinates for the latest release of jackson-databind and jackson-datatype-eclipse-collections.

To start out with Jackson we create an ObjectMapper:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.eclipsecollections.EclipseCollectionsModule;

ObjectMapper mapper = new ObjectMapper().registerModule(new EclipseCollectionsModule());

We are now ready to serialize data:

public class MyClass {
    // The fields of this class must be public, or must have getters and
    // setters. For simplicity we will stick with public fields here.
    public String text;
    public int value;
}

MyClass object = new MyClass();
object.text = "foo";
object.value = 42;
String json = mapper.writeValueAsString(object);
// json = {"text":"foo","value":42}

MyClass deserialized = mapper.readValue(json, MyClass.class);

Combining the two

Now we are ready to serialize our Eclipse Collections! Lets start with a simple example:

public class EclipseClass {
    public ImmutableList<String> strings;
}

EclipseClass object = new EclipseClass();
object.strings = Lists.immutable.of("foo", "bar");
String json = mapper.writeValueAsString(object);
// json = {"strings":["foo","bar"]}

EclipseClass deserialized = mapper.readValue(json, EclipseClass.class);

As you can see, the list was serialized to a JSON array, as expected. Lets try with a primitive collection:

public class EclipseClassInt {
    public IntList ints;
}

EclipseClassInt object = new EclipseClassInt();
object.ints = IntLists.immutable.of(1, 2, 3);
String json = mapper.writeValueAsString(object);
// json = {"ints":[1,2,3]}

EclipseClassInt deserialized = mapper.readValue(json, EclipseClassInt.class);

Using the jackson-datatype-eclipse-collections module primitive collections are treated just like normal collections.

Serializing directly

Sometimes you do not want to write your own type wrapper but want to serialize collections directly. Because of generics, this takes some extra work to deserialize in Jackson:

MutableList<String> mutable = Lists.mutable.of("foo", "bar");
String json = mapper.writeValueAsString(mutable);
// json = ["foo","bar"]

MutableList<String> deserialized = mapper.readValue(json, new TypeReference<MutableList<String>>() {});

Because Java does not have a syntax like MutableList<String>.class, we have to stick with the more convoluted Jackson type references. Do not attempt to use MutableList.class instead - you will get an error that does not immediately make clear what the issue is.

For primitive collections, because they do not use generics, using .class is possible:

IntList intList = IntLists.immutable.of(1, 2, 3);
String json = mapper.writeValueAsString(intList);
// json = [1,2,3]

IntList deserialized = mapper.readValue(json, IntList.class);

Compact serialization

The Eclipse Collections module for Jackson attempts to be consistent in the serialization of primitive collections with their array counterparts. IntList will serialize like an int[] would, CharList like a char[] would and so on. For char and byte this can make for a more compact representation. ByteList is serialized as base64 strings (or a binary type for non-JSON formats):

ByteList byteList = ByteLists.immutable.of(new byte[]{ 73, 20, 32, 23 });
String json = mapper.writeValueAsString(byteList);
// json = "SRQgFw=="

Similarly, CharList will just be serialized to a JSON string:

CharList charList = CharLists.immutable.of('f', 'o', 'o');
String json = mapper.writeValueAsString(charList);
// json = "foo"

For CharList, this feature can be turned off in Jackson:

CharList charList = CharLists.immutable.of('f', 'o', 'o');
String json = mapper.writer(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)
        .writeValueAsString(charList);
// json = ["f","o","o"]