Can not convert JSON to domain object with spring restTemplate - json

Actually I try to invoke a get request with the restTemplate in Spring. Debbuging my application clearly shows that the JSON is downloaded but the automatic mapping does not work. My List of domain object includes only 0 values and null values.
When I invoke the get request from the browser, I get the following response as JSON (I copied here the first two record out of the 3 192):
[{"OrderId":77862,"DateAdded":"2016-04-30T02:25:40.263","OrderItemCorpusId":"HUBW","OrderItemCorpusOriginalId":null,"OrderItemCurrency":"HUF","OrderItemExchangeRate":1.00000,"OrderItemOriginalLocation":"HU","OrderItemBuyLocation":"HU","OrderItemPrice":1337.80314,"OrderItemDiscountId":0,"OrderItemDiscountValue":"","DocumentId":25140,"Title":"Romana Gold 10. kötet","PublisherOriginalName":"Harlequin Kiadó","ISBN":"9789634073369"},{"OrderId":77864,"DateAdded":"2016-04-30T15:49:22.61","OrderItemCorpusId":"HUBW","OrderItemCorpusOriginalId":null,"OrderItemCurrency":"HUF","OrderItemExchangeRate":1.00000,"OrderItemOriginalLocation":"HU","OrderItemBuyLocation":"HU","OrderItemPrice":2748.03149,"OrderItemDiscountId":0,"OrderItemDiscountValue":"","DocumentId":25252,"Title":"Az eltűnt lány","PublisherOriginalName":"Harlequin Kiadó","ISBN":"9789634072423"}]
My POJO domain object which should keep the converted data from JSON:
#JsonIgnoreProperties(ignoreUnknown = true)
public class BandWTransaction {
private long OrderId;
private Date DateAdded;
private String OrderItemCurrency;
private double OrderItemExchangeRate;
private String OrderItemBuyLocation;
private double OrderItemPrice;
private String OrderItemDiscountValue;
private long DocumentId;
private String Title;
private String PublisherOriginalName;
private String ISBN;
//getters and setters
Finally the code snippet I use for the rest get request:
String startDate = new SimpleDateFormat("yyyy-MM-dd").format(start.getTime());
String endDate = new SimpleDateFormat("yyyy-MM-dd").format(end.getTime());
UriComponents uri = UriComponentsBuilder.newInstance().scheme("http").host("www.bookandwalk.hu")
.path("/api/AdminTransactionList").queryParam("password", "XXX")
.queryParam("begindate", startDate).queryParam("enddate", endDate).queryParam("corpusid", "HUBW")
.build().encode();
LOG.log(Level.INFO, "{0} were called as a rest call", uri.toString());
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.set("User-Agent", "Anything");
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<List<BandWTransaction>> transResponse = restTemplate.exchange(uri.toString(), HttpMethod.GET,
entity, new ParameterizedTypeReference<List<BandWTransaction>>() {
});
List<BandWTransaction> transactions = transResponse.getBody();
When I debug the app I realized that the transactions list includes objects with full of null and 0 values. More precisely, there is no and objcet within the list having other values as 0 and null in the properties.
I have also checked that spring boot automatically registered in the restTemplate.messageConverters ArrayList 9 HttpMessageConverter. The 7th element of this ArrayList is the org.springframework.http.converter.json.MappingJackson2HttpMessageConverter which supports the application/json and application/+json media types.
Any idea is appreciated to solve this problem as I am newbie in spring and in JSON mapping in general.

It seems you have a naming convention issue due to your field variables starts with a uppercase. When Jackson finds a pair getTitle/setTitleasumes that the name of this variable is title (starting with lowercase). Of course, if you change the capitalization of your variables, json properties and java variables has different names, so mapping still fails. The best solution is change your names to meet Java conventions, and use Jackson annotations to define your mappings.
#JsonProperty(value="OrderId")
private int orderId;
#JsonProperty(value="DateAdded")
private Date dateAdded;
Hope it helps.

I can suggest you to write a test and check how fasterxml ObjectMapper read a json and unmarshall json to your object:
ObjectMapper objectMapper = new ObjectMapper();
String somestring = objectMapper.readValue("somestring", String.class);
just replace String with your class and "somestring" with your json. So you check if there is problem with it.
And try to use #JsonPropery cause all this capital letters fields start with looks messy:
#JsonIgnoreProperties(ignoreUnknown = true)
public class BandWTransaction {
#JsonProperty("OrderId")
private long OrderId;
[...]
With this stuff I read json correct. You can come in from other side remove ignoring unknown properties:
#JsonIgnoreProperties(ignoreUnknown = true) //remove it and run test
public class BandWTransaction {
and you get :
(11 known properties: "dateAdded", "orderItemExchangeRate",
"documentId", "orderItemPrice", "orderId", "orderItemBuyLocation",
"orderItemDiscountValue", "orderItemCurrency", "isbn", "title",
"publisherOriginalName"])
So problem in variables naming and you can fix it with #JsonProperty

Related

Eliminate duplicate Json elements and retrieve element names starting with capital letters spring boot/java

I'm developing a Rest Client using Spring Boot and Spring Framework (spring-boot-starter-parent 2.1.6.RELEASE)
I have a class representing a response object as shown below:
public class ValidateResponse {
private String ResponseCode;
private String ResponseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
I'm creating a web-hook for an external api and I need to return a JSON object to for a specific endpoint (the JSON object properties must start with uppercase(s)). I'm calling returning the object from a PostMapping method nested in a RequestMapping root path:
#PostMapping("hooks/validate")
public ValidateResponse responseObj(#RequestHeader Map<String, String> headersObj) {
ValidateResponse response = new ValidateResponse("000000", "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return response;
}
However, when I hit the endpoint from postman I'm getting duplicate varialbes
{
"ResponseCode": "000000",
"ResponseDesc": "Success",
"responseCode": "000000",
"responseDesc": "Success"
}
I understand that the pojo-json conversion is handled by spring but I don't understand why the conversion is yielding duplicate variables.
Note: I know the ResponseDesc and the ResponseCode are not declared using the best standards for naming variables (camelCasing).
I've done some digging and according to the Java Language Specification
An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
and
The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII underscore (_, or \u005f) and dollar sign ($, or \u0024). The $ character should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems.
So, I'm assuming its syntactically correct to define a variable using the Camelcase format [Need clarification on this].
I'm considering having to create the JSON object manually but I'd like to know the cause of this behaviour first. Any pointers are appreciated.
Jackson deserializes all the public fields that it comes across. However if you want Jackson to return the response in your expected element names (in your case elements starting with capital letters), make the fields private and annotate them with the #JsonProperty(expected_name_here). Your class file will typically looks as shown below
public class ValidateResponse {
#JsonProperty("ResponseDesc")
private String responseCode;
#JsonProperty("ResponseDesc")
private String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
Note: The getters and setters for these fields should be public, otherwise Jackson won't see anything to deserialize in the class.
public class ValidateResponse {
#JsonProperty("ResponseDesc")
public String responseCode;
#JsonProperty("ResponseDesc")
public String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
This must fix your problem, however I do not know the reason as it requires deep Jackson investigation.
EDIT
I found out the reason.
The field got duplicated because in you case you had:
2 public fields named in upper case -> they are to be processed by jackson
2 getters getResponseCode and getResponseDesc -> they are to be resolved
as accessors for properties responseCode and responseDesc accordingly.
Summing this up - you have 4 properties resolved by Jackson. Simply making your fields private will resolve your issue, however I still advise using JsonProperty approach.
I added a com.google.code.gson dependency in the projects pom.xml file to configure Spring Boot to use Gson (instead of the default jackson).
The Json object returned from the hooks/validate endpoint must have its property names starting with a capital letter. Using a java class to generate the response object was resulting to camelCased property names so I resolved to create the JSON response object manually. Here's the code for creating the custom JSON object:
public ResponseEntity<String> responseObj(#RequestHeader Map<String, String> headersObj) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
JsonObject response = new JsonObject();
response.addProperty("ResponseCode", "00000000");
response.addProperty("ResponseDesc" , "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());
}
Note The JSON object is returned as a String so the response from the endpoint must have an additional header to define MediaType to inform the calling system that the response is in JSON format:
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
then add the header to the response:
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());

Deep copy of properties one object to anothe from jackson?

I am performing an UPDATE operation such that all the non-null properties set in the incoming POJO shall be copied into another object (same type) and this shall happen for nested properties too.
Ex:
POJO:
public class Person {
private String homePhoneNumber;
private String officePhoneNumber;
private Address address;
public String getHomePhoneNumber() {
return homePhoneNumber;
}
// getter/setters
}
public class Address {
private String street;
private String houseNumber;
public String getStreet() {
return street;
}
// getter/setters
}
// Source
Person sourcePerson = new Person();
sourcePerson.setHomePhone("123");
Address address1 = new Address();
address1.setStreet("Street");
sourcePerson.setAddress(address1);
//Dest person
Person destPerson = new Person();
destPerson.setOfficePhone("456");
destPerson.setHomePhone("123");
Address address2 = new Address();
address2.setStreet("Street2");
address2.setHouseNumber("246");
destPerson.setAddress(address2);
ObjectMapper mapper = new ObjectMapper();
//skip setters for null values
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
Person result = mapper.updateValue(destPerson, sourcePerson);
So I want to copy all non-null properties set in sourcePerson to get copied in destPerson i.e overriding only those properties of destperson which sourcePerson has set, keeping other properties unchanged.
Using
Person result = mapper.updateValue(destPerson, sourcePerson);
is not working for nested properties. It's replacing the whole Address object from source to destination
I searched through jackson to find merge feature in jackson which:
mapper.setDefaultMergeable(true);
However, adding this configuration is making null values in sourcePerson nullify those in destPerson too, which seems strange.
mapper.configOverride(Address.class).setMergeable(true);
This above configuration works for what I wanted. But I have many POJO nested resources, so I don't want specific configurations for each POJO.
Is this can be achieved with jackson in a clean way ?
You can start by enabling error reporting with respect to merging
com.fasterxml.jackson.databind.MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE
This needs to be false.
It's indeed strange that mapper.configOverride() sort of works, but not mapper.setDefaultMergeable().
I don't see setters in your example. Aren't you using #JsonSetter annotations in Person class by any chance? Then they would override mapper configuration.
In jackson-databind Unit Tests I see they are using mapper.readerForUpdating() rather than mapper.updateValue():
private final ObjectMapper MAPPER = objectMapperBuilder()
// 26-Oct-2016, tatu: Make sure we'll report merge problems by default
.disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
.build();
public void testBeanMergingWithNullDefault() throws Exception
{
// By default `null` should simply overwrite value
ConfigDefault config = MAPPER.readerForUpdating(new ConfigDefault(5, 7))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config);
assertNull(config.loc);
// but it should be possible to override setting to, say, skip
// First: via specific type override
// important! We'll specify for value type to be merged
ObjectMapper mapper = newObjectMapper();
mapper.configOverride(AB.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
config = mapper.readerForUpdating(new ConfigDefault(137, -3))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config.loc);
assertEquals(137, config.loc.a);
assertEquals(-3, config.loc.b);
// Second: by global defaults
mapper = newObjectMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
config = mapper.readerForUpdating(new ConfigDefault(12, 34))
.readValue(aposToQuotes("{'loc':null}"));
assertNotNull(config.loc);
assertEquals(12, config.loc.a);
assertEquals(34, config.loc.b);
}
Also worth trying using com.fasterxml.jackson.annotation.JsonMerge directly in Person class.

asp web api json serialization returns private properties

I have a Web api controller class
and i call a method on a proxy class generated from a WSDL from the web api controller,
and i return an object of type defined in the WSDL proxy class.
but the xml/json returned contains the private members of the proxy class
the private members are
private string companyField;
private string soldBPField;
private string fromDateField;
private string toDateField;
private long succStatusField;
and the xml returned is :
<companyField>700</companyField>
<soldBPField>999000353</soldBPField>
<fromDateField>01-01-2012</fromDateField>
<toDateField>01-01-2013</toDateField>
the json returned is also similar
I changed the xml formatter in the global.asax as follows:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
this changed the XML returned to:
<company>700</company>
<soldBP>999000353</soldBP>
<fromDateField>01-01-2012</fromDate>
<toDate>01-01-2013</toDate>
but the json returned still contains the name of the private variables
can any one help me with this
Are your types marked as [Serializable]? Serializable means that the serializer should serialize all fields - private or public. Try to remove Serializable or else use this line to stop the JSON formatter from recognizing the attribute:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();

How to map java object attribute(my_name) with json attribute (my-name)?

I am using jackson json api to map json data to java objects. All is well in case of same object attribute names with json attributes. Now i have a situation where i am getting json data attribute with -. (my-name).
In java we can't include - in variable names.
import org.codehaus.jackson.map.ObjectMapper;
private static final ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue("{my-name:\"abcd\"}", User.class);
public class User {private String my_name; /*get-set methods*/}
Is there anything i need to apply in User.class.
I don't want to change my code so much.
In your java class you can give any name as you like
Ex. private String myName;
But in the setter method just write:
#JsonProperty("my-name")
public void setMyName(String myName) {
this.myName = myName;
}

How to serialize JSON with Object containing both primitives and List<> types?

So I have a class that I was planning on using for simple JSON serialization.
public class Thing {
private int field1;
private String some_other_field;
private List<SubType> subs;
private list<AnotherType> another;
public String toJson() {
Gson g = new Gson();
g.toJson(this);
}
}
So the documentation shows that if you want to serialize generic types, you need to specify a TypeToken:
Type listtype = new TypeToken<List<SubType>>() {}.getType();
gson.toJson(subs, listtype);
But then, how does this work if I have a whole class I want to serialize for? Where do I get to specify the serialization type for those two List<> types so that I can just pass the whole object in and get a serialized output? Is that even possible?
From the doc it seems that if you serialize a complete object with toJson(...), it deals with the generics attributes properly.
toJson(Object)
"Note that this method works fine if the any of the object fields are of generic type, just the object itself should not be of a generic type"
What output did you get with your object ?