0

I want to deserialize the following JSON input:

{
  "key1": "value1",
  "key2": "value2"
}

into a class object containing a hash map:

public class ClassContainingMap {

  private Map<String, String> map = new HashMap<>();

  public Map<String, String> getMap() {
     return map;
  }

  public void setMap(Map<String, String> map) {
     this.map = map;
  }
}

When executing

ClassContainingMap m = objectMapper.readValue(json, ClassContaininMap.class);

I get

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of test.ClassContainingMap: no Stri
ng-argument constructor/factory method to deserialize from String value ('key1')
 at [Source: "key1": "value1", "key2": "value2"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
    at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
    at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:370)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:315)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1283)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2858)

I found that I can get the other direction (serialization) working when annotating the getter with @JsonValue, but so far I haven't been able to figure this out for deserialization. Is there any simple (annotation?) way to achieve this, without having to write a custom deserializer?

dflachbart
  • 105
  • 1
  • 10

1 Answers1

1

You did not provide an example how you use the @JsonValue annotation so I might have missed something, but anyway.

When serializing I think you do something like:

ClassContainingMap ccm = new ClassContainingMap();
ccm.getMap().put("key1", "value1");
ccm.getMap().put("key2", "value2");

System.out.println(om.writeValueAsString(ccm)); 

This will work and will produce the following JSON:

{
    "map": {
        "key1":"value1",
        "key2":"value2",
    }
}

But this does not equal to the JSON structure you want to read to ClassContainingMap:

{
    "key1": "value1",
    "key2": "value2"
}

Namely the former is data from ClassContainingMap and the latter is "only" Map that I think you want to populate inside ClassContainingMap.

You have two options to check, you can;

  1. use the correct JSON structure as a source that corresponds ClassContainingMap
  2. just deserialize this to a simple map, like:

    Map map = om.readValue(i, Map.class);
    
  3. and if either of above are possible, one way is to do it with @JsonCreator

    @JsonCreator(mode=Mode.DELEGATING)
    public ClassContainingMap(@JsonProperty("map")Map<String,String> map) {
        this.map = map;
    }
    
    @JsonValue
    public Map<String, String> getMap() {
        return map;
    }
    

See some more information about 3rd for example here

pirho
  • 11,565
  • 12
  • 43
  • 70
  • Regarding the serialization - if I annotate `public Map getMap()` with `@JsonValue` then it gets serialized into the desired JSON structure. Regarding `1` - unfortunately not possible, as it's JSON coming in as a POST payload, and `2` - yeah that's what I'm probably end up doing. Not a big deal, just wanted to see if there might be any annotation that would achieve the equivalent for deserialization (I understand that this is very specific use case here because the class only has one single map member). In any case, thanks! – dflachbart Sep 06 '18 at 13:54
  • @dflachbart Updated answer a bit. It is a matter of taste -I think - if 3rd is more convenient that 2nd. – pirho Sep 06 '18 at 17:36
  • Maybe you can use [`@JsonUnwrapped`](https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html)? – fps Sep 06 '18 at 17:47
  • @FedericoPeraltaSchaffner Yeah I tried that, but it doesn't seem to work on Map – dflachbart Sep 10 '18 at 13:56
  • @pirho Great, thanks for pointing out `JsonCreator`, that works! – dflachbart Sep 10 '18 at 13:57