Jackson: Deserialize map with non String keys
See original GitHub issueI’m trying to deserialize a map that contains keys that are not Strings. They do not seem to be deserialized properly.
For instance the library I am interfacing with, treats all strings as the binary type. (The encoding of the bytes not guaranteed to be utf-8 which is why they are not using the string type.)
So assuming I have a map of {a:1, b:2, c:3}
where a, b and c are binary payloads. The following code tries to deserialize that into a Map<byte[], Integer>
ByteArrayOutputStream out = new ByteArrayOutputStream();
MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(3);
for (int i = 0; i < 3; i++) {
messagePacker
.packBinaryHeader(1).writePayload(new byte[]{(byte) (i + 'a')})
.packInt(i);
}
messagePacker.close();
ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
Map<byte[], Integer> map = objectMapper.readValue(
out.toByteArray(), new TypeReference<Map<byte[], Integer>>() {});
This results in an exception
com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [array type, component type: [simple type, class byte]]
at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:608)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:169)
at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:462)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:231)
at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:653)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:444)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3666)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3558)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2688)
A quick search of this on google looks like I would need to add a KeyDeserializer. http://stackoverflow.com/questions/11246748/deserializing-non-string-map-keys-with-jackson. So I created a SimpleModule and registered it with the ObjectMapper.
ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
SimpleModule module = new SimpleModule();
module.addKeyDeserializer(byte[].class, new KeyDeserializer() {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws
IOException,
JsonProcessingException {
throw new IOException("KeyDeserializer Called");
}
});
objectMapper.registerModule(module);
Map<byte[], Integer> map = objectMapper.readValue(
out.toByteArray(), new TypeReference<Map<byte[], Integer>>() {});
However it doesn’t appear the KeyDeserializer ever called called and the returned map is empty.
This is also a problem with Integer keys (and I assume all other non string keys)
ByteArrayOutputStream out = new ByteArrayOutputStream();
MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(3);
for (int i = 0; i < 3; i++) {
messagePacker
.packInt(i)
.packInt(i);
}
messagePacker.close();
ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
Map<Integer, Integer> map = objectMapper.readValue(
out.toByteArray(), new TypeReference<Map<Integer, Integer>>() {});
Exception is thrown without the KeyDeserializer. Adding the KeyDeserializer the map is empty.
(Side comment (and possibly another issue): Serializing a Map<Integer, Integer>
with jackson actually outputs a map of Map<String, Integer>
on the msgpack side which is undesirable.)
Issue Analytics
- State:
- Created 8 years ago
- Comments:5 (4 by maintainers)
@fdinoff In https://github.com/msgpack/msgpack-java/issues/271, jackson-dataformat-msgpack supports Boolean, Integer and Float type key while byte array key seemed to work before the change.
BTW, you tried to use
Map<byte[], Integer> map
but I don’t think it’s easy to usebyte[]
as a key of Map sincebyte[]#equals
doesn’t match other byte arrays which have the same values.@komamitsu Sorry, didn’t realize that
byte[]
was a valid key now.The problem now is what happens if the bytes weren’t valid utf8? is the conversion to String and back to a byte array lossless? The doc for String#getBytes() says its unspecified, so the conversion isn’t lossless. Losing valid data would be undesirable.
Also I don’t think you can deserialize into an actual object. (Is there?)
Example: You have the following class
I would expect something like the following to work (the byte array is utf16 encoded.