@JsonUnwrapped creates non-null object even when all fields are null

Viewed 651

I ran into this problem in the context of SpringBoot, but it seems to be just a Jackson issue.

I have a DataObject which has several fields. The DataObject is contained within a Model. All the fields of DataObject are optional. But if all the fields are null, then I want the dataoObject itself to be null, but that doesn't seem to be the way it works.

Here my sample code

@Getter
@ToString
@NoArgsConstructor
public class TestContract {

    private String field1;

    @JsonUnwrapped
    @Valid
    private DataObject dataObject;

    public static void main(String[] args) throws JsonProcessingException {
        //language=JSON
        String json = "{ \"field1\" :  \"value1\"}";
        ObjectMapper mapper = new ObjectMapper();
        TestContract contract = mapper.readValue(json, TestContract.class);
        System.out.println("contract: " + contract);
    }
}


@Getter
@NoArgsConstructor
@ToString
public class DataObject {
    private String nested1;
    private String nested2;
}

The output I end up with is

contract: TestContract(field1=value1, dataObject=DataObject(nested1=null, nested2=null))

Is there any way to have the object end up as null?

1 Answers

The issue is due to the fact that the JsonUnwrapped annotation has the enabled property with a default true value, so even if the property dataObject is not present in your json message an empty dataObject object will be created in any case. To solve the issue, you could use a JsonUnwrapped(enabled = false) annotation or you could directly delete the JsonUnwrapped annotation.

Update : due to the fact that according to the project's specifics is it not possible to erase the JsonUnwrapped annotation, a workaround is the creation of a custom deserializer leaving untouched the main code like below :

@Getter
@ToString
@NoArgsConstructor
public class TestContract {

    private String field1;

    @JsonUnwrapped
    private DataObject dataObject;

    public static void main(String[] args) throws JsonProcessingException, IOException {
        //language=JSON
        String json = "{ \"field1\" :  \"value1\"}";
        ObjectMapper mapper = new ObjectMapper();
        TestContract contract = mapper.readValue(json, TestContract.class);
        System.out.println("contract: " + contract);
    }
}


@Getter
@Setter
@NoArgsConstructor
@ToString
@JsonDeserialize(using = DataObjectDeserializer.class)
public class DataObject {
    private String nested1;
    private String nested2;
}

public class DataObjectDeserializer extends JsonDeserializer<DataObject> {
    @Override
    public DataObject deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        String nested1 = node.get("nested1").asText();
        String nested2 = node.get("nested2").asText();
        DataObject dataObject = new DataObject();
        dataObject.setNested1(nested1);
        dataObject.setNested2(nested2);
        return dataObject;
    }
}
Related