I have this class
public static class SomeClass {
public SomeClass(String field) {
this.field = field;
}
private final String field;
public String getField() {
return field;
}
}
I have also this test (edited)
@Test
public void testStringifyMapOfObjects() {
Map<String, SomeClass> original = Maps.newTreeMap();
original.put("first", new SomeClass("a"));
original.put("second", new SomeClass("b"));
String encoded = JsonUtil.toJson(original);
Map<String, SomeClass> actual = JsonUtil.fromJson(encoded, Map.class);
Assert.assertEquals("{'first':{'field':'a'},'second':{'field':'b'}}", encoded.replaceAll("\\s", "").replaceAll("\"", "'"));
Assert.assertEquals(original.get("first"), actual.get("first"));
}
The test fails with
junit.framework.AssertionFailedError: expected:<eu.ec.dgempl.eessi.facade.transport.test.TestToolTest$SomeClass@6e3ed98c> but was:<{field=a}>
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.failNotEquals(Assert.java:277)
at junit.framework.Assert.assertEquals(Assert.java:64)
at junit.framework.Assert.assertEquals(Assert.java:71)
at eu.ec.dgempl.eessi.facade.transport.test.TestToolTest.testStringifyMapOfObjects(TestToolTest.java:90)
Can I make json to properly serialize objects as the values of the map or should I use something else?
edited
public class JsonUtil {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(JsonUtil.class);
public static <T> String toJson(T data) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.INDENT_OUTPUT, true);
try {
return mapper.writeValueAsString(data);
} catch (IOException e) {
LOG.warn("can't format a json object from [" + data + "]", e);
return null;
}
//
// return Json.stringify(Json.toJson(data));
}
public static <T> T fromJson(String description, Class<T> theClass) {
try {
JsonNode parse = new ObjectMapper().readValue(description, JsonNode.class);
T fromJson = new ObjectMapper().treeToValue(parse, theClass);
return fromJson;
} catch (JsonParseException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
} catch (JsonMappingException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
} catch (IOException e) {
// throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
LOG.warn("can't parse a json object from [" + description + "]", e);
return null;
}
}
}
You are running into a problem related to Java generics. To summarize, when deserializing data into a non-reifiable type (aka a type for which actual type information is not available at runtime) you need to use a supertype token. You can get more detail about what a supertype token is (and why you need to use one) by reading these SO posts:
And also from the Jackson documentation:
The basic problem is that when you use a typical generic object, the actual type parameters for the object aren't available at runtime. Therefore Jackson doesn't know which actual class to instantiate and deserialize your data into.
The easiest way to get around the problem would be adding an overload to your JSON utility class, that accepts a type reference (as opposed to a
Class<T>
). For example:To be used as such:
I discovered that the simplest solution is to create a "container" class that will contain the map. This is working probably because the container has enough type details for the map, as opposed to the case when a map is used directly.
After this ... the updated test is