X. Wo Satuk X. Wo Satuk - 5 months ago 34
JSON Question

Deserializing Guava's Table with Jackson throws "JsonMappingException: No suitable constructor found for type"

I'm using

jackson-datatype-guava
. I'm trying to deserialize a Table from JSON, but I'm getting an exception.

My code is:

@Test
public void jsonTable() throws IOException {
Table<String, String, String> table = HashBasedTable.create();
table.put("row1", "col1", "cell11");
table.put("row1", "col2", "cell12");
table.put("row2", "col1", "cell21");
table.put("row2", "col2", "cell22");

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

String tableJson = mapper.writeValueAsString(table);
System.out.println(tableJson);

Table<String, String, String> deserializedTable = mapper.readValue(tableJson, HashBasedTable.class);
}


The exception thrown at the last line is:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.google.common.collect.HashBasedTable]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {"row1":{"col2":"cell12","col1":"cell11"},"row2":{"col2":"cell22","col1":"cell21"}}; line: 1, column: 2]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:255)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1130)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:298)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:133)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3807)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2797)
at com.gk.dfm.repository.impl.JsonTableTest.jsonTable(JsonTableTest.java:31)


It makes sense, since HashBasedTable has no default constructor. We should construct it using
HashBasedTable.create()
. How can I tell it to Jackson though?

On this page I found how to deserialize Guava's Multimap:

@Test
public void convert() throws JsonParseException, JsonMappingException,
JsonProcessingException, IOException {

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new GuavaModule());

Multimap<String, NavItem> navs = objectMapper.readValue(
objectMapper.treeAsTokens(objectMapper.readTree(jsonString)),
objectMapper.getTypeFactory().constructMapLikeType(
Multimap.class, String.class, NavItem.class));

logger.info(navs);

assertThat(navs.keys(), hasItems("123455", "999999"));
}


How can I apply it to a Table? There is no such thing as constructTableLikeType.

Answer

First, try using jackson-datatypes-collections instead of jackson-datatype-guava, because it has been moved under jackson-datatype-collections to simplify management, release process.

The support for Guava's Table seems to be incomplete, because there's an open issue for adding (de)serializer for it. Since it's open source, you can do it yourself (please contribute back if you do so!) - you can use what other other people wrote, like this pull request in old repository (also see test for Table which indicates serializing is supported, but no deserializing though).

Comments