javajsonb-api

Looking for an explanation of this curious behaviour during de-/serialization of enums


Recently I came across a curious behaviour during de-/serialization of enums to/from JSON.

I dumbed down the relevant part into a simple example (see code below). basically you have a list that contains enum entries. You serialize the list and then deserialize it. if you check that new (deserialized) list if it contains certain enum entries you will always get 'false' as an answer, even though the list actually contains the entry.

after analysing it a bit myself i found out that after deserializing the list, the content is no longer of the enum type, but contains strings.

i would like to understand why this happens and how i can avoid it. my goal would be to have a list of enum entries again after deserialization and not a list of strings.

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;

public class Test {

    enum TYPE {
        SMALL, MEDIUM, LARGE
    }

    public static void main(String[] args) {

        List<TYPE> myList = new ArrayList<>();

        myList.add( TYPE.MEDIUM );

        // serialize
        String serializedJsonString = JsonbBuilder.create(new JsonbConfig().withLocale(Locale.GERMAN).withFormatting(true)).toJson(myList);

        // deserialize
        @SuppressWarnings("unchecked")
        List<TYPE> mySecondList = JsonbBuilder.create(new JsonbConfig().withLocale(Locale.GERMAN).withFormatting(true)).fromJson(serializedJsonString, List.class);

        System.out.println( myList.contains( TYPE.MEDIUM ) );       // will be true (as expected)

        System.out.println( mySecondList.contains( TYPE.MEDIUM ) ); // will be false (surprising!)
        System.out.println( mySecondList.contains( "MEDIUM" ) );    // will be true (surprising!)
    }
}

Solution

  • Java enums are serialized to Strings by default, as there is no better default JSON type for them. (You can configure them to be serialized to ints if you prefer.)

    When you deserialize, you have to instruct the Jsonb parser to convert the JSON Strings into the enum, otherwise it will deserialize them to Java Strings by default.

    You have not done that in your example above, because you asked it for a List.class (i.e. List<?>), rather than a List of TYPE enums (List<TYPE>). This is tricky to do in Java because of type erasure. (Notice that you had to mark the code as "unchecked", because the compiler knows that your version won't work.) The JsonB docs discuss this case explicitly: http://json-b.net/docs/user-guide.html#mapping-a-generic-collection

    Try something like:

    List<TYPE> mySecondList = jsonb.fromJson(result, new ArrayList<TYPE>(){}.getClass().getGenericSuperclass());