0

I have created a simple test case that shows the error. Please let me know if I've missed something.

I have a simple interface that declares a single accessor:

public class JacksonParsingTest {
    public interface EventWithMap {
        Map<String, String> getContextMap();
    }

    @Test
    public void testJsonMapDeserializes()
        throws JsonParseException, JsonMappingException, IOException
    {
        String json = "{\"contextMap\":[" +
                  "{\"key\":\"processedAt\",\"value\":\"Thu Dec 31 14:30:51 EST 2015\"}" +
                  "]}";

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule( new MrBeanModule() );
        EventWithMap e = mapper.readValue( json, EventWithMap.class );

        assertEquals( "Thu Dec 31 14:30:51 EST 2015", e.getContextMap().get( "processedAt" ) );
    }
}

When I run this unit test, Jackson fails on parsing the json string:

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token
at [Source: {"contextMap":[{"key":"processedAt","value":"Thu Dec 31 14:30:51 EST 2015"}]}; line: 1, column: 2] (through reference chain: RecoveryEventBuilderTest$EventWithMap["contextMap"])

As you can see from the stacktrace, Jackson correctly extracts the contextMap token from the json but it fails to continue tokenizing the string. As far as I know, the json string correctly represents a map.

Thanks, Robin.

Robin Coe
  • 750
  • 7
  • 28
  • What concrete class should Jackson use to unmarshall the JSON to the EventWithMap interface? Moreover, a Map containing "foo" as a key and "bar" as a value is normally represented in JSON as `{"foo": "bar"}` – JB Nizet Jan 04 '16 at 19:04
  • I am using the MrBeanModule (mapper.registerModule( new MrBeanModule() );), which is responsible for materializing POJO beans from interfaces. And a map is an array of objects in json, i.e., [{},{},{}]. This JSON string was also created by Jackson, so I expect it's valid and should be parseable by Jackson. – Robin Coe Jan 04 '16 at 19:10
  • 1
    That must be another side effect of this module, because [this code](https://gist.github.com/jnizet/9c7c991d0f92f6362ce7) produces `json = {"contextMap":{"processedAt":"Thu Dec 31 14:30:51 EST 2015"}}`. Maybe you should post a complete example containing the marshalling code as well, that produces the JSON. – JB Nizet Jan 04 '16 at 19:16
  • Very interesting indeed. So there's no array token in the ObjectMapper serializer in your case. Mine was actually not created with MrBeanModule, as far as I know, it was created with log4j2, which uses Jackson for its JSONLayout appender. I will investigate whether this is a bug in that implementation. Thanks. – Robin Coe Jan 04 '16 at 19:24
  • 1
    According to the log4j2-dev group, this representation was done purposely to make writing XML and JSON consistent: http://mail-archives.apache.org/mod_mbox/logging-log4j-dev/201601.mbox/%3CCAOC1H_uwW9SfoD5CKpqOqEvYNw%2B%3DFtPNbdWWA8Qqtv567VpFxw%40mail.gmail.com%3E – Robin Coe Jan 04 '16 at 20:31

1 Answers1

0

As others pointed out, the problem is that JSON in question is structurally incompatible with the POJO: there is that unnecessary extra Array wrapper.

But there is a way to handle this particular case: if you enable DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, you should be able to get conversion for the specific case where array has one AND EXACTLY ONE value in it.

StaxMan
  • 113,358
  • 34
  • 211
  • 239