Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jackson desearlization: two keys at root. how do i unwrap one and ignore the other?

Tags:

java

json

jackson

using jackson 2.x

the json response looks like this:

{
 "flag": true,
 "important": {
   "id": 123,
   "email": "[email protected]"
 }
}

The "flag" key does not provide any useful information. I would like to ignore the "flag" key and unwrap the "important" value to an instance of Important.

public class Important {

    private Integer id;
    private String email;

    public Important(@JsonProperty("id") Integer id,
                     @JsonProperty("email") String email) {
        this.id = id;
        this.email = email;
    }

    public String getEmail() { this.email }

    public Integer getId() { this.id }
}

When I try to add a @JsonRootName("important") to Important and configure the ObjectMapper with DeserializationFeature.UNWRAP_ROOT_VALUE I receive a JsonMappingException:

Root name 'flag' does not match expected ('important') for type ...

When i remove the "flag" key/value from the JSON the data binding works just fine. I get the same result if i add @JsonIgnoreProperties("flag") to Important as well.

UPDATES


updated class ... that will actually pass the compile step
@JsonRootName("important")
public static class Important {
    private Integer id;
    private String email;

    @JsonCreator
    public Important(@JsonProperty("id") Integer id,
                     @JsonProperty("email") String email) {
        this.id = id;
        this.email = email;
    }

    public String getEmail() { return this.email; }

    public Integer getId() { return this.id; }
}

actual test:

@Test
public void deserializeImportant() throws IOException {
    ObjectMapper om = new ObjectMapper();
    om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    om.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
    Important important = om.readValue(getClass().getResourceAsStream("/important.json"), Important.class);

    assertEquals((Integer)123, important.getId());
    assertEquals("[email protected]", important.getEmail());
}

results:

com.fasterxml.jackson.databind.JsonMappingException: Root name 'flag' does not match expected ('important') for type [simple type, class TestImportant$Important]

like image 879
carlos Avatar asked Nov 10 '22 01:11

carlos


1 Answers

Just because of streaming nature of JSON parsing in Jackson, I'm afraid there is no easy way of handling such cases.

From my point of view, it's easier to do with some sort of wrapper.

Consider this code:

public static class ImportantWrapper {
    @JsonProperty("important")
    private Important important;

    public Important getImportant() {
        return important;
    }
}

And actual test:

@Test
public void deserializeImportant() throws IOException {
    ObjectMapper om = new ObjectMapper();
    //note: this has to be present
    om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    Important important = om.readValue(getClass().getResourceAsStream("/important.json"), ImportantWrapper.class)
                                .getImportant();

    assertEquals((Integer)123, important.getId());
    assertEquals("[email protected]", important.getEmail());
}

Note, that @JsonRootName("important") is redundant and can be removed in this case.

This looks some sort of ugly, but works perfectly with relatively small effort. Also such "wrappers" can be generified, but this is more like architecture's stuff.

like image 183
n1ckolas Avatar answered Nov 15 '22 04:11

n1ckolas