Search code examples
javajacksondeserializationobjectmapper

Jackson ObjectMapper - How to exclude field when value is false after de-serialized from remote response?


I am making a remote call to external service A which would return the response as json and then using object mapper to de-serialize into MyResponse object. After that in my current service, I need to attach this object and output to the UI.

One of the field in MyResponse from service A is a boolean and I only want my UI response to include this field when the value is true. Note that I don't have access to modify my MyResponse object as it was read-only. So I created a MixIn class also tried couple of ways but it didn't work..

    public class MyResponse {

        private String stringValue;
        private int intValue;
        // Expectation: only include this field when value true, and exclude it when value is false
        private boolean booleanValue;
    }

    // @JsonIgnoreProperties(value = { "booleanValue" })
    // @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY)
    private static class MixInMyResponse {

    }

    // this would be my rest service eventually send myResponse to UI
    public MyResponse readFromRemote() throws IOException {
        String jsonAsString =
            "{\"stringValue\":\"a\",\"intValue\":1,\"booleanValue\":false}";
        ObjectMapper mapper = new ObjectMapper();
        // configure object mapper with mix in
        mapper.getDeserializationConfig().addMixInAnnotations(MyResponse.class, MixInMyResponse.class);
        MyResponse myResponse = mapper.readValue(jsonAsString, MyResponse.class);
        // Expectation: writeValue needs only include booleanValue when value true, and exclude booleanValue when value is false
        String writeValue = mapper.writeValueAsString(myResponse);
        System.out.println(writeValue);
        return myResponse;
    }

  1. Using @JsonIgnoreProperties(value = { "booleanValue" }): this would do the trick when value is false, but also it doesn't include the field when value is true
  2. Using @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY): when value is false, this would deserialized field booleanValue as false, so my returned myResponse/writeValue will still have this field to UI.

Is there any additional suggestion on that?


Solution

  • Write your own Serializer

    public class MyResponseSerializer extends JsonSerializer<MyResponse> {
        @Override
        public void serialize(MyResponse myResponse, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeStringField("stringValue", myResponse.getStringValue());
            jsonGenerator.writeNumberField("intValue", myResponse.getIntValue());
            if (myResponse.isBooleanValue()) {
                jsonGenerator.writeBooleanField("booleanValue", myResponse.isBooleanValue());
            }
            jsonGenerator.writeEndObject();
        }
    }
    

    Register it with the ObjectMapper

    @Test
    public void test3() throws IOException {
        MyResponse response1 = new MyResponse("response1", 1, true);
        MyResponse response2 = new MyResponse("response2", 1, false);
    
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(MyResponse.class, new MyResponseSerializer());
        objectMapper.registerModule(module);
        System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response1));
        System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response2));
    }
    

    See this tutorial

    I edited the original answer when I saw you couldn't modify MyResponse to add an annotation.