Multiple DTOs manual initialize - java

in Microservice, we post multiple dtos data as string json.
Controller:
#RequestMapping(value="/json",method = RequestMethod.POST)
public String getjson(#RequestBody String json) {
///Service process
}
Post Json:
{
"dtos":{
"Dto1":{
"name":"Dto1 Name Field",
"filter":[
{"key":"f1","value":1},
{"key":"f2","value":10}
]
},
"Dto2":{
"city":"Newyork",
"filter":[
{"key":"f1","value":1},
{"key":"f2","value":10},
{"key":"f3","value":10}
]
}
},
"page":1
}
DTO:
public class Dto1{
private String name;
}
public class Dto2{
private String city;
}
Dto1 and Dto2 is java DTO object name.
how to convert string json to java objects?

You can create a new DTO that contains all attrs and receive in request:
public class Filter{
private String key;
private int value;
}
public class Dto1{
private String name;
private List<Filter> filter;
}
public class Dto2{
private String city;
private List<Filter> filter;
}
public class Dtos{
public Dto1 dto1;
public Dto2 dto2;
}
public class DtoToReceiveInRequest{
private Dtos dtos;
private int page;
}
Controller
#PostMapping
public String getjson(#RequestBody DtoToReceiveInRequest json) {
///Service process
}

You can use the ObjectMapper from the jackson library, like below.
String json = "";
ObjectMapper objectMapper = new ObjectMapper();
Dto1 dto = objectMapper.readValue(json, Dto1.class);
But in your particular example, you don't have to have two DTO classes. You can encapsulate values in one DTO and have the list of different instances of that DTO in a json format.
NB. The json string should be a representation of the preferred class you want to retrieve, eg Dto1.java.

Related

How to deserialize the generic response using Spring WebClient

I have a problem during the deserialization of a response. Let's suppose I have this response from third party using webclient .
Response :
{
"name":"FirstName",
"type":"Steel",
"Fee":{
"id":"1234",
"name":"FeeFirstName"
},
"address":"2nd Street"
}
This is how my pojo classes looks like
public class Fee{} //generic OR empty class
public class Foo{
private String name;
private String type;
private Fee fee;
private String address;
}
My webclient get response code :
#Autowired
private WebClient fooWebClient;
public Foo getFoo()
{
try{
return fooWebClient.get()
.uri(uriBuilder -> uriBuilder.path("/foo/fee").build("123"))
.header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Foo.class)
.block();
}catch(Exception e){throw new ApiClientException(e.getMessage());}
}
The above webclient getFoo() code is not giving me the full response, the Fee is coming blank stating "Class has no fields". Rest of the values are coming properly in response.
Fee needs to be empty as any other object can also come.
Please let me know how to deserialize the whole response.
You don't need the Fee class, you can get rid of it entirely and use a Map instead:
public class Foo {
private String name;
private String type;
private Map<String, Object> fee;
private String address;
}
We cannot dynamically create POJO and hence we are left with two options.
Add necessary fields to the 'Fee' class (If you know Fee structure upfront)
If you are not sure about the 'Fee' structure go for Map.
Because spring integrates Jackson you can create a custom Jackson JSON Deserializer for the Fee class that gives you more control:
#JsonDeserialize(using = FeeDeserializer.class)
public class Fee {
private String id;
private String name;
public Fee(String id, String name) {
this.id = id;
this.name = name;
}
}
import com.fasterxml.jackson.*;
public class FeeDeserializer extends JsonDeserializer<Fee> {
#Override
public Fee deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
ObjectCodec codec = jsonParser.getCodec();
JsonNode tree = codec.readTree(jsonParser);
JsonNode id = tree.get("id");
JsonNode name = tree.get("name");
return (id != null && name != null) ? new Fee(id.asText(), name.asText()) : null;
}
}
For more details see
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.json.jackson.custom-serializers-and-deserializers
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-codecs-jackson

Creating complex JSON payload from Java Pojo Jackson

I want to create below JSON payload
{
"maxResults":3,
"counter":0,
"customerParameters":{
"filters":[
{
"name":"customerId",
"operator":"=",
"value":["hello"]
}
]
},
"dealerParameters":[
{
"name":"club"
},
{
"name":"token"
}
]
}
Coded so far:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"maxResults",
"counter",
"customerParameters",
"dealerParameters"
})
public class CustomerModel {
#JsonProperty("maxResults")
private Integer maxResults;
#JsonProperty("counter")
private Integer counter;
#JsonProperty("customerParameters")
private CustomerParameters customerParameters;
#JsonProperty("dealerParameters")
private List<DealerParameter> dealerParameters = null;
#JsonProperty("customerParameters")
public CustomerParameters getCustomerParameters() {
return customerParameters;
}
#JsonProperty("customerParameters")
public void setCustomerParameters(CustomerParameters customerParameters) {
this.customerParameters = customerParameters;
}
#JsonProperty("dealerParameters")
public List<DealerParameter> getDealerParameters() {
return dealerParameters;
}
#JsonProperty("dealerParameters")
public void setDealerParameters(List<DealerParameter> dealerParameters) {
this.dealerParameters = dealerParameters;
}
// Getter/Setter for other params
}
CustomerParameters.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"filters"
})
public class CustomerParameters {
#JsonProperty("filters")
private List<Filter> filters = null;
// Setter and Getter for filters parameter
}
DealerParameters.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"name"
})
public class DealerParameter {
#JsonProperty("name")
private String name;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
}
Filter.java
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"name",
"operator",
"value"
})
public class Filter {
#JsonProperty("name")
private String name;
#JsonProperty("operator")
private String operator;
#JsonProperty("value")
private List<String> value = null;
#JsonProperty("value")
public List<String> getValue() {
return value;
}
#JsonProperty("value")
public void setValue(List<String> value) {
this.value = value;
}
// Setter and Getter for other properties
}
Missing Part:
#Controller
public class TestContoller {
RestTemplate restTemplate;
Should I instantiate each pojo class with new operator as below and set all required parameters ? or any other approach of creating JSON payload?
CustomerModel customerModel= new CustomerModel();
customerModel.setMaxResults(1);
Filter filter= new Filter();
filter.setName("customerID");
filter.setOperator("-");
filter.setValue(Arrays.asList("club"));
CustomerParameters customerParameters = new CustomerParameters();
customerParameters.setFilters(Arrays.asList(filter));
customerModel.setCustomerParameters(customerParameters);
For DealerParameter class, I want to create multiple objects with same key different value(see the json payload I mentioned above). Below code creates only one object "name":"dealerId"
DealerParameter dealerParameter = new DealerParameter();
dealerParameter.setName("dealerId");
customerModel.setDealerParameters(dealerParameter);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValueAsString(customerModel);
restTemplate.exchange(todo); // restful service call
}
you are already using "ObjectMapper", And ObjectMapper has readValue() method. By using readValue() method you can populate all data at a time like below:--
ObjectMapper objectMapper = new ObjectMapper();
//populating data from json string to POJO
CustomerModel customerModel = objectMapper.readValue(<json String>,CustomerModel.class);
System.out.println(objectMapper.writeValueAsString(customerModel); // print all data

Why are some of the variables in POJO equal to null after converting JSON RESTful Webservice?

I am consuming a RESTful webservice that returns a JSON payload. I can successfully consume the RESTful webservice and manage to populate some of the POJO attributes with JSON data. However, some other attributes are null when they are supposed to contain a value. How can I ensure that there are no more nulls?
I have defined 4 POJO classes. I have so far debugged by systematically by testing the variables for each class. This is using Springboot 2.2.0 and Jackson-databind.
The JSON schema I am trying to consume:
{
"items":[
{
"timestamp":"2019-09-18T16:42:54.203Z",
"carpark_data":[
{
"total_lots":"string",
"lot_type":"string",
"lots_available":"string"
}
]
}
]
}
For the above, I defined 4 classes:
public class Response {
#JsonProperty
private List<items> i;
#JsonIgnoreProperties(ignoreUnknown = true)
public class items {
private String timestamp;
private List<carpark_data> cpd;
#JsonIgnoreProperties(ignoreUnknown = true)
public class carpark_data {
private List<carpark_info> cpi;
private String carpark_number;
private String update_datetime;
#JsonIgnoreProperties(ignoreUnknown = true)
public class carpark_info {
private int total_lots;
private String lot_type;
private int lots_available;
When I run the below in Spring boot Main: I get null. Is my POJO modeling OK?
Response resp = restTemplate.getForObject("")
c = resp.getItems().get(0).getCarpark_data().get(0);
log.info("The last update time for the car park data = " +
c.getUpdateDatetime());
Your model does not fit to JSON payload. If we assume that JSON payload has a structure like below:
{
"items": [
{
"timestamp": "2019-09-18T16:42:54.203Z",
"carpark_data": [
{
"total_lots": "1000",
"lot_type": "string",
"lots_available": "800"
}
]
}
]
}
We can deserialise it as below:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Response response = mapper.readValue(jsonFile, Response.class);
System.out.println(response.getItems().get(0).getData().get(0));
}
}
class Response {
private List<Item> items;
//getters, setters, toString
}
class Item {
private String timestamp;
#JsonProperty("carpark_data")
private List<CarParkInfo> data;
//getters, setters, toString
}
class CarParkInfo {
#JsonProperty("total_lots")
private int totalLots;
#JsonProperty("lot_type")
private String lotType;
#JsonProperty("lots_available")
private int lotsAvailable;
//getters, setters, toString
}
Above code prints:
CarParkInfo{totalLots=1000, lotType='string', lotsAvailable=800}
Hope you find the solution.
It is in POJO, you need to check the fieldName and object structure.
Seeing the Json above, your response model returns list of items and in each item you have list of carpark_data. So, basic modelling should be like this. And you can include respective setter and getter.
public class Response {
#JsonProperty
private List<items> items;
#JsonIgnoreProperties(ignoreUnknown = true)
public class items {
private String timestamp;
private List<carpark_data> carpark_data;
#JsonIgnoreProperties(ignoreUnknown = true)
public class carpark_data {
private int total_lots;
private String lot_type;
private int lots_available;
}
You need to have fields name in POJO class same in the Json response or you can set JsonProperty for that field. Like this
#JsonProperty("items")
private List<items> i;
#JsonProperty("carpark_data")
private List<carpark_data> cpd;

Can get field from inner json object with jackson?

I have json like that:
{
"somethingElse": "foobar",
"snils": {
"number": "123"
}
}
And class:
#Data
public class Documents {
private String snilsNumber;
private String somethingElse;
}
Can I easily map json to my class with annotation or something else?
You can use '#JsonRootName'
#Data
#JsonRootName(value = "snils")
#JsonIgnoreProperties(unknown = true)
public class Documents {
private String number;
}
You can deserialise it using one extra update method with JsonProperty annotation.
class Documents {
private String snilsNumber;
private String somethingElse;
#JsonProperty("snils")
private void unpackSnils(Map<String, Object> brand) {
this.snilsNumber = (String) brand.get("number");
}
// getters, setters, toString
}
See also:
Jackson nested values
unwrap inner json object using jackson

Deserialize nested array as ArrayList with Jackson

I have a piece of JSON, that looks like this:
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
I have created a class to store Author information:
public class Author {
#JsonProperty("given-name")
public String givenName;
public String surname;
}
And two wrapper classes:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
This is working, but having two wrapper classes seems to be unnecessary. I want to find a way to remove Authors class and have a list as a property of Entry class. Is something like that is possible with Jackson?
Update
Solved that with custom deserializer:
public class AuthorArrayDeserializer extends JsonDeserializer<List<Author>> {
private static final String AUTHOR = "author";
private static final ObjectMapper mapper = new ObjectMapper();
private static final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
#Override
public List<Author> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectNode objectNode = mapper.readTree(jsonParser);
JsonNode nodeAuthors = objectNode.get(AUTHOR);
if (null == nodeAuthors // if no author node could be found
|| !nodeAuthors.isArray() // or author node is not an array
|| !nodeAuthors.elements().hasNext()) // or author node doesn't contain any authors
return null;
return mapper.reader(collectionType).readValue(nodeAuthors);
}
}
And using it like this:
#JsonDeserialize(using = AuthorArrayDeserializer.class)
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
Thanks #wassgren for the idea.
I see at least two approaches to do this if you want to get rid of wrapper classes. The first is to use the Jackson Tree Model (JsonNode) and the second is to use a deserialization feature called UNWRAP_ROOT_VALUE.
Alternative 1: Use JsonNode
When deserializing JSON using Jackson there are multiple ways to control what type of objects that are to be created. The ObjectMapper can deserialize the JSON to e.g. a Map, JsonNode (via the readTree-method) or a POJO.
If you combine the readTree-method with the POJO conversion the wrappers can be completely removed. Example:
// The author class (a bit cleaned up)
public class Author {
private final String givenName;
private final String surname;
#JsonCreator
public Author(
#JsonProperty("given-name") final String givenName,
#JsonProperty("surname") final String surname) {
this.givenName = givenName;
this.surname = surname;
}
public String getGivenName() {
return givenName;
}
public String getSurname() {
return surname;
}
}
The deserialization can then look something like this:
// The JSON
final String json = "{\"authors\":{\"author\":[{\"given-name\":\"AdrienneH.\",\"surname\":\"Kovacs\"},{\"given-name\":\"Philip\",\"surname\":\"Moons\"}]}}";
ObjectMapper mapper = new ObjectMapper();
// Read the response as a tree model
final JsonNode response = mapper.readTree(json).path("authors").path("author");
// Create the collection type (since it is a collection of Authors)
final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
// Convert the tree model to the collection (of Author-objects)
List<Author> authors = mapper.reader(collectionType).readValue(response);
// Now the authors-list is ready to use...
If you use this Tree Model-approach the wrapper classes can be completely removed.
Alternative 2: remove one of the wrappers and unwrap the root value
The second approach is to remove only one of the wrappers. Assume that you remove the Authors class but keep the Response-wrapper. If you add the a #JsonRootName-annotation you can later unwrap the top-level name.
#JsonRootName("authors") // This is new compared to your example
public class Response {
private final List<Author> authors;
#JsonCreator
public Response(#JsonProperty("author") final List<Author> authors) {
this.authors = authors;
}
#JsonProperty("author")
public List<Author> getAuthors() {
return authors;
}
}
Then, for your mapper simply use:
ObjectMapper mapper = new ObjectMapper();
// Unwrap the root value i.e. the "authors"
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final Response responsePojo = mapper.readValue(json, Response.class);
The second approach only removes one of the wrapper classes but instead the parsing function is quite pretty.

Categories