Home > front end >  @RequestBody that accepts both List and Single object
@RequestBody that accepts both List and Single object

Time:09-16

I have this code for a RestAPI (simplified for the purpose of this question).

public class UserRequestDTO {
    
    private Long userId;
    
    private List<Email> email;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public List<Email> getEmail() {
        return email;
    }

    public void setEmail(List<Email> email) {
        this.email = email;
    }

}


@RestController
public class UserController {

    @PostMapping(value = "/user", produces = "application/json")
    public ResponseEntity<UserResponseDTO> addUser(@RequestBody UserRequestDTO userRequestDTO) {

        ....
    }

}

The above code sucessfully creates userRequestDTO for the following POST request body (Postman) that has several emails:

{
    "email": [
      {
      "type": "primary",
      "value": "[email protected]"
      },
      {
      "type": "primary",
      "value": "[email protected]"
      }
    ] 
}

But it does not create userRequestDTO for this other POST request body that has just one email:

{
  "email": {
    "type": "primary",
    "value": "[email protected]"
   } 
}

The thing is that I have a requirement to make both type of requests (one and several emails) work.

I cannot edit the json postman request. That's how it comes. I need the change to be in the Java code.

How to do that?

Thanks.

CodePudding user response:

It is possible to configure deserialization in Jackson. For example:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfiguration {
   @Bean
   @Primary
   public ObjectMapper objectMapper() {
      return new ObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
   }
}

CodePudding user response:

A little bit odd requirements to be fair but is not impossible this is what I could think of a custom Deserializer , thanks to Jackson is really easy to implement , the main idea behind this realization is that inside your code email will always be an array , but it will be assembled differently base on incoming JSON , this is mine POJO

@Data
@AllArgsConstructor
@JsonDeserialize(using = AgentSerializer.class)
public class Agent {

    private Long id;

    private List<String> name;
}

and based on this I have a custom deserializer

package com.mihai.learn.domain;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


public class AgentSerializer extends JsonDeserializer<Agent> {

    private ObjectMapper mapper = new ObjectMapper();

    @Override
    public Agent deserialize(final JsonParser jsonParser,
                             final DeserializationContext deserializationContext) throws IOException, JacksonException {


        final TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
        final Long id = Long.parseLong(treeNode.get("id").toString());
        final TreeNode name = treeNode.get("name");

        boolean areMultipleName = name.isArray();

        final List<String> names = new ArrayList<>();

        if (areMultipleName) {
            final List<String> strings = mapper.readValue(name.toString(), new TypeReference<>() {
            });
            names.addAll(strings);
        } else {
            final String s = mapper.readValue(name.toString(), String.class);
            names.add(s);
        }

        return new Agent(id, names);
    }
}

I checked it works with both type of request

{
    "id": 100,
    "name": "name"
}

and

{
    "id":100,
    "name":[
        "name",
        "name2"
    ]
}

CodePudding user response:

For the benefit of others, I found another solution that is even simpler than the solution proposed by @Alexander Kashpirovsky that I checked as the accepted one.

public class UserRequestDTO {   
    ... 
    @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    private List<Email> email;
    ...
}
  • Related