How to omit null field from Swagger/OpenAPI in ResponseEntity?
Asked Answered
B

4

17

I'm trying to omit null values in my ResponseEntity.

My controller looks something like this:

@RestController
public class FooController {

    //fields
    //constructor

    @PostMapping
    public ResponseEntity<CreateFooResponseV10> createFoo(@Valid @RequestBody CreateFooRequestV10 foo, HttpServletRequest request) {
        //some minor logic
        return new ResponseEntity<>(aFooResponseV10Builder()
                .withFirstName(foo.getFirstName())
                .withLastName(foo.getLastName())
                .withTestField(NULLABLE_OBJECT)
                .build(), ...);
    //I generated the builders from the output classes openapi-generator provided
    }
    // more stuff...
}

When NULLABLE_OBJECT is equal to null I expect the field to be omitted from the response like this:

{
  "firstName": "John",
  "lastName": "Doe"
}

But I either get these responses, depending on what I've tried so far:

{
  "firstName": "John",
  "lastName": "Doe",
  "testField": null
}

or

{
   "firstName": "John",
   "lastName": "Doe",
   "testField": {"present":false}
}

I generate my request/response objects (CreateFooResponseV10 and CreateFooRequestV10) with the use of openapi-generator

Here is my redacted api.json file:

{
  "openapi": "3.0.1",
  "info": { ... },
  "servers": [ ... ],
  "paths": {
    "/foo": {
      "post": {
        ...
        "requestBody": {
          "description": "Foo to be created",
          "content": {
            "application/foo+json;version=1.0": {
              "schema": {
                "$ref": "#/components/schemas/CreateFooRequest_V1_0"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Foo is successfully created",
            "headers": { ... },
            "content": {
              "application/foo+json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateFooResponse_V1_0"
                }
              }
            }
          },
          ...
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CreateFooRequest_V1_0": {
        "required": [
          "firstName",
          "lastName"
        ],
        "type": "object",
        "properties": {
          "firstName": { ... },
          "lastName": { ... },
          "testField": {
            "description": "...",
            "type": "string",
            "nullable": true
          }
        }
      },
      "CreateFooResponse_V1_0": {
        "required": [
          "firstName",
          "lastName"
        ],
        "type": "object",
        "properties": {
          "firstName": { ... },
          "lastName": { ... },
          "testField": {
            "description": "...",
            "type": "string",
            "nullable": true
          }
        }
      }
    }
  }
}

As you can see in both the request and response testField is not required and can be nullable. So when testField is null it should be hidden from the response, but when it contains some date it should be shown of course.

I've tried overriding jackson's ObjectMapper bean as explained in this answer. Didn't work.
I've tried adding spring.jackson.default-property-inclusion=non_null to the application.properties. Didn't work.

What I think should work is adding @JsonIgnore above testField of the generated classes, but I don't know if this is something needed to be done manually (for each schema component, can be a lot of manual work for something that is generated) or if this can be configured in the plugin somewhere.

Thanks in advance.


extra info
OpenAPI 3.0.1
Maven 3.6.3
Java 11.0.2
jackson-databind-nullable 0.2.1
openapi-generator-maven-plugin 4.2.2

Brat answered 18/2, 2020 at 0:38 Comment(0)
C
18

You can set the following in application.properties

spring.jackson.default-property-inclusion = NON_NULL

See Customize the Jackson ObjectMapper

Note: To make use of this, you need to @Autowire the ObjectMapper, and not manually create it

Cana answered 30/7, 2020 at 21:24 Comment(5)
Thanks, also the solution by Popov works, but in that case I had to fix also the conversion in json text of the dates in OffsetDateTime generated by default settings.Gesticulation
its not working for me with spring boot version 2.1.4 I use application.yml file spring: jackson: default-property-inclusion: NON_NULLHygrometric
@Hygrometric It works for me only if I autowire the ApiClient Bean instead of creating with new.Fungoid
It doesn't workFairground
Using the classes generated by openapi and making certain fields nullable, all I had to do was add this spring configPharyngitis
P
9

You can generate the model classes with additional class annotations using OpenApi generator. Just need to include this in your maven plugin:

<configOptions>
   <additionalModelTypeAnnotations>
@com.fasterxml.jackson.annotation.JsonInclude(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
   </additionalModelTypeAnnotations>
<configOptions>

see other config options here: https://openapi-generator.tech/docs/generators/spring/

Pascasia answered 23/6, 2022 at 17:3 Comment(0)
R
6

Try registering the following bean in your spring context. It should override default bean

@Bean
public HttpMessageConverters httpMessageConverters() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(Include.NON_NULL)
    return new HttpMessageConverters(
            new MappingJackson2HttpMessageConverter(mapper));
}
Rost answered 18/2, 2020 at 6:35 Comment(1)
Thanks. It's working. I am also searching if someway we can add config properties while open API code generator so that no need to include bean.Backache
H
-3

Try this code. I tested and it works.

@RestController
@RequestMapping("/testws")
public class TestWS {
    @RequestMapping(value = "test", method = { RequestMethod.POST,
            RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ResponseEntity<TestBean> test(@Context HttpServletRequest request) {
        TestBean testBean = new TestBean("John", "Doe", null);
        return ResponseEntity.ok()
                .body(testBean);
    }
}

@JsonInclude(Include.NON_NULL)
class TestBean {
    private String firstName;
    private String lastName;
    private String testField;

    public TestBean() {

    }

    public TestBean(String firstName, String lastName, String testField) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
        this.testField = testField;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getTestField() {
        return testField;
    }

    public void setTestField(String testField) {
        this.testField = testField;
    }

}

Json response:

{"firstName":"John","lastName":"Doe"}
Henderson answered 18/2, 2020 at 2:38 Comment(1)
Not helpful, as described in the question the request&response objects are automatically generated with Swagger and therefore adding a manual annotation is not possible.Taliesin

© 2022 - 2024 — McMap. All rights reserved.