Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson handles case when synchronized HashMap as class member

Tags:

java

gson

I have a case where synchronized HashMap as class member.

public class Code {
    private Code(String code) {
        this.code = code;
    }

    public static Code newInstance(String code) {
        if (code == null) {
            throw new java.lang.IllegalArgumentException("code cannot be null");
        }

        return new Code(code);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + code.hashCode();

        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof Code)) {
            return false;
        }

        return this.code.equals(((Code)o).code);
    }

    @Override
    public String toString() {
        return code;
    }

    private String code;    
}

public class AlertStateManager {
    public boolean addFallBelow(Code code) {
        fallBelows.put(code, System.currentTimeMillis());
        return true;
    }

    public boolean addRiseAbove(Code code) {
        riseAboves.put(code, System.currentTimeMillis());
        return true;
    }

    public boolean removeFallBelow(Code code) {
        return fallBelows.remove(code) != null;
    }

    public boolean removeRiseAbove(Code code) {
        return riseAboves.remove(code) != null;
    }

    public void remove(Code code) {
        fallBelows.remove(code);
        riseAboves.remove(code);
    }

    public void remove() {
        fallBelows.clear();
        riseAboves.clear();
    }

    private final Map<Code, Long> fallBelows = java.util.Collections.synchronizedMap(new HashMap<Code, Long>());
    private final Map<Code, Long> riseAboves = java.util.Collections.synchronizedMap(new HashMap<Code, Long>());    
}

When I perform serialization and de-serialization, I get the following

public static void main(String[] args) {
    AlertStateManager alertStateManager = new AlertStateManager();
    alertStateManager.addFallBelow(Code.newInstance("hello"));
    alertStateManager.addRiseAbove(Code.newInstance("world"));

    String json_alert_state_manager = null;
    // WRITE
    {
        final Gson gson = new Gson();
        json_alert_state_manager = gson.toJson(alertStateManager);
    }

    System.out.print(json_alert_state_manager);

    // READ
    {
        final Gson gson = new Gson();
        alertStateManager = gson.fromJson(json_alert_state_manager, AlertStateManager.class);
    }
}

Serialization

{"fallBelows":{"hello":1370356891664},"riseAboves":{"world":1370356891664}}

De-serialization

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 17
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186)
    at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172)
    at com.google.gson.Gson.fromJson(Gson.java:803)
    at com.google.gson.Gson.fromJson(Gson.java:768)
    at com.google.gson.Gson.fromJson(Gson.java:717)
    at com.google.gson.Gson.fromJson(Gson.java:689)
    at javaapplication6.JavaApplication6.main(JavaApplication6.java:38)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 17
    at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:374)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:165)
    ... 10 more
Java Result: 1

Anything I can do to make it works? I'm using Gson 2.2.3

like image 496
Cheok Yan Cheng Avatar asked Nov 20 '25 04:11

Cheok Yan Cheng


2 Answers

@MikO gives good hint. After several experiment, I found that during serialization, we need to construct the following

        GsonBuilder builder = new GsonBuilder();
        builder.enableComplexMapKeySerialization();
        Gson gson = builder.create(); 

This will generate correct json string.

{"fallBelows":[[{"code":"hello"},1370359852472]],"riseAboves":[[{"code":"world"},1370359852472]]}

Note that, during de-serialization, to avoid the above json string turned into LinkedHashMap, here is what we need to do

private static class SynchronizedMapInstanceCreator<K, V> implements
        InstanceCreator<Map<K, V>> {

    @Override
    public Map<K, V> createInstance(final Type type) {
        return java.util.Collections.synchronizedMap(new HashMap<K, V>());
    }
} 

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    String json_alert_state_manager = "{\"fallBelows\":[[{\"code\":\"hello\"},1370359852472]],\"riseAboves\":[[{\"code\":\"world\"},1370359852472]]}";
    // READ
    {
        Gson gson = new GsonBuilder().registerTypeAdapter(
            new TypeToken<Map<Code, Long>>() {}.getType(), 
            new SynchronizedMapInstanceCreator<Code, Long>()).create();
        AlertStateManager alertStateManager = gson.fromJson(json_alert_state_manager, AlertStateManager.class);
        alertStateManager.debug();
    }
}
like image 129
Cheok Yan Cheng Avatar answered Nov 22 '25 16:11

Cheok Yan Cheng


As you are telling Gson that it should parse the content of "fallBelows" (and also "riseAboves") element as a Map<Code, Long>, it is expecting something like:

"fallBelows": { {codeObject}, someLong }

But it finds this:

"fallBelows": { "someString", someLong }

And that's why it complains saying that it expeced an object (Code), but it found a string...

EDIT: I've just realized that this is probably clear for you, but there must be an issue in serialization of maps, I'll try to check...

like image 31
MikO Avatar answered Nov 22 '25 18:11

MikO



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!