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
@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();
}
}
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With