8

Given my own array implementation MyArray<T>, how can I make it known to Jackson, so that it is able to deserialize from a JSON Array into MyArray<T>? So far I am only getting this exception:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of MyArray out of START_ARRAY token
5
  • 8
    More code is needed, post the MyArray class. Commented Mar 8, 2016 at 12:53
  • It is LibGDX' Array implementation: github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/… Commented Mar 8, 2016 at 12:57
  • also where the deserialization happen maybe Commented Mar 8, 2016 at 12:57
  • 2
    Probably @Dariusz suspects the same thing i do. The problem is that your class does not extend array, but has an array as instance variable which is where the array operations happen. your JSON must reflect MyArray's structure (an object with an array inside would probably work). This is the problem i think, i don't know if there's some way to tell Jackson to treat your class as if it was just an array (but i hope someone can give you the answer) Commented Mar 8, 2016 at 13:01
  • 1
    You can write your custom deserializer for Jackson. I never tried it with a custom array though but it should also work. Commented Mar 8, 2016 at 13:17

3 Answers 3

3
+50

As Dariusz mentioned, it's good to take advantage of the fact that Array class has constructor accepting normal array.

Look, if you use default serializer - your array serialized to JSON would look like:

{"items":["item1","item2"],"size":2,"ordered":true}

it's clearly a waste of space, unless you want size and ordered fields to be preserved.

I suggest you changing the way you serialize your object so that it would look more like normal array, on the other end - deserialization can build Array object again.

If you add following pair of serializer and deserializer:

SimpleModule module = new SimpleModule();
module.addDeserializer(Array.class, new StdDelegatingDeserializer<>(
    new StdConverter<Object[], Array>() {
        @Override
        public Array convert(Object[] value) {
            return new Array(value);
        }
}));

module.addSerializer(Array.class, new StdDelegatingSerializer(
    new StdConverter<Array, Object>() {
        @Override
        public Object convert(Array value) {
            return value.toArray();
        }
}));

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

you will have transparent conversion between these types

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! I like this one the most.
2

The Array class from libgdx has a constructor which accepts an array: public Array (T[] array).

Instead of trying to serialize libgdx array use a simple class with an array as a base for serialization/desrialization, and then create a libgdx array based on the deserialized data.

In general it is a good rule to serialize only POJO-type objects.

In short:

{
     //serialize:
     com.badlogic.gdx.utils.Array<MyObj> arr = ...;
     MyObj[] myArr = arr.toArray();
     MyCustomContainer cont = new MyCustomContainer(myArr);
     String serializedData = mapper.writeValueAsString(cont);
     // do sth with the data
}
{
    //deserialize
    MyCusomContainer cont = mapper.readValue(..., MyCustomContainer.class);
    com.badlogic.gdx.utils.Array<MyObj> arr = new com.badlogic.gdx.utils.Array<MyObj>(cont.getArray());
    // done!
}

2 Comments

That is what I am doing right now, however I thought it would be more elegant to use the LibGDX array directly, instead of converting back and forth.
You would have to modify the code for libgdx Array, I guess - mark only custom methods for serialization with appropriate annotations. Or use an XmlAdapter, I think it works for json, I'm not sure though.
2

One way to do it is to write a serializer like

import java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.ser.std.SerializerBase;

public class MyArraySerializer extends SerializerBase<MyArray> {

    protected MyArraySerializer() {
        super(MyArray.class);
    }

    @Override
    public void serialize(MyArray myArray, JsonGenerator gen, SerializerProvider p)
            throws IOException, JsonGenerationException {
        gen.writeStartArray();
        Iterator<MyObject> it = myArray.iterator();
        while (it.hasNext()) {
            MyObject ob = it.next();
            gen.writeObject(p);
            if (it.hasNext()) {
                gen.writeRaw(',');
            }
        }
        gen.writeEndArray();
    }
}

And a deserializer like

import java.io.IOException;

import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;

public class MyArrayDeserializer extends JsonDeserializer<MyArray> {

    @Override
    public MyArray deserialize(JsonParser parser, DeserializationContext ctx)
            throws IOException, JsonProcessingException {
        MyObject[] obs = parser.readValueAs(MyObject[].class);
        return new MyArray(obs); //presuming you have a copy-constructor
    }
}

Then annotate the property that holds such an array with @JsonSerialize(using = MyArraySerializer.class) @JsonDeserialize(using = MyArrayDeserializer.class).
If you use your array implementation directly, instead of inside a container class, this page has an example of how to register serialization handlers at run-time http://wiki.fasterxml.com/JacksonHowToCustomSerializers

I should note that in this answer I am using the Jackson 1.9 API and the 2.x may be slightly different. According to http://wiki.fasterxml.com/JacksonUpgradeFrom19To20 the most noticeable differences are the changes in package names and where some classes are located. Otherwise this code should be unaffected.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.