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;
...
}