how to add custom objectMapper for a specific spring rest controller - json

I have two controllers in my micro service both are POST and accepts Request body as JSON, one is working fine and another one's JSON input from some othet team and it is with root class name , so I need to write custom object mapper for this later controller, could you please guys help,
please find the codes below,
#RestController
#Slf4j
public class Controller2 {
#RequestMapping(value = "/some/update", method = RequestMethod.POST)
public String updateEmd(#RequestBody final UpdateEMDRequest updateEMDRequest) throws JsonProcessingException {
updateEMDRequest.getBookingReference()); // null now
return "success";
}
}
and the sample json is as follows,
{
"UpdateEMDRequest":{
"TransactionStatus":"SUCCESS",
"UniqueTransactionReference":"046060420",
"PreAuthReference":"040520420",
"BookingReference":"8PJ",
"CarrierCode":"AS",
"TransactionMode":"Batch",
"CallBackUrl":"www.test.com/op/update",
"Offers":[
{
"Offer":{
"traveler":{
"firstName":"AHONY",
"surname":"DNEN",
"EMD":[
"081820470"
]
}
}
}
]
}
}
UpdateEMDRequest,java
#JsonInclude(Include.NON_NULL)
public class UpdateEMDRequest {
#JsonProperty("UniqueTransactionReference")
private String uniqueTransactionReference;
#JsonProperty("TransactionStatus")
private String transactionStatus;
#JsonProperty("PreAuthReference")
private String preAuthReference;
#JsonProperty("BookingReference")
private String bookingReference;
#JsonProperty("CarrierCode")
private String carrierCode;
#JsonProperty("TransactionMode")
private String transactionMode;
#JsonProperty("CallBackUrl")
private String callBackUrl;
#JsonProperty("Offers")
private List<Offers> offers;
}
So this json is not parsed properly and updateEMDRequest's properties are null always.

Related

Spring - How to convert a String representation of Date in JSON body field in request model comming into controller to OffsetDateTime

I have legacy data coming in to my API as part of UserRequest model like
#PostMapping
public MyResponse saveUser(#Valid #RequestBody UserRequest userRequest) {
...
}
UserRequest class uses OffsetDateTime for dateRegistered field:
public class UserRequest {
...
OffsetDateTime birthDate;
...
}
The problem I am having is that the data is coming into the API using below format for dateRegistered field:
{
"last-name":"Mikel",
"birth-date":"20200716"
}
So, the string representation "20200716" from JSON request needs to be somehow converted to OffsetDateTime like "2020-07-16T00:00:00Z" (the time portion and offset is set to 0s).
By default, the "20200716":
{
"last-name":"Mikel",
"birth-date":"20200716"
}
, gets converted to OffsetDateTime value of
{
"last-name": "Mikel",
"birth-date": "1970-08-22T19:18:36Z"
}
, which is obviously way off.
How do I convert a string representation of date in Json field like "20200716" to its OffsetDateTime representation like "2020-07-16T00:00:00Z" or "2020-07-16T00:00:00.000+00:00"?
I was trying to annotate my OffsetDateTime field with #JsonFormat("yyyyMMdd") but that is throwing exception like: JSON parse error: Cannot deserialize value of type java.time.OffsetDateTime from String "20200716".
you don't need a JSON annotation. You need to adjust the setter as follow.
public class MedicalCandidateRequest {
private OffsetDateTime dateRegistered;
public OffsetDateTime getDateRegistered() {
return dateRegistered;
}
public void setDateRegistered(String dateString) {
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSxx";
DateTimeFormatter dtFormatter = DateTimeFormatter.ofPattern(pattern);
this.dateRegistered = OffsetDateTime.parse(dateString, dtFormatter );
}
}
Change the parameter of the setter method to a String and do the conversion yourself.
public void setDateRegistered(String value) {
this.dateRegistered = doConversionHere(value);
}
Thanks for suggestions but I have decided to go with my own implementation.
I provided a custom deserializer like:
public class CustomDateDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final String PATTERN = "yyyyMMdd";
private final DateTimeFormatter formatter;
public CustomDateDeserializer() {
this.formatter = DateTimeFormatter.ofPattern(PATTERN);
}
#Override
public OffsetDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JacksonException {
LocalDate localDate = LocalDate.parse(parser.getText), formatter);
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneOffset.UTC);
return offsetDateTime;
}
}
, which I then use to annotate my model field like:
#JsonDeserialize(using = CustomDateDeserializer.class)
private OffsetDateTime birthDate;

Spring RestTemplate response body is null when trying to parse JSON

I've been trying many solutions from similar solved problems in this page but I can't make it work. I'm making a get petition to obtain an array JSON, and I want to map those values into my class.
I got this class:
public class Devices {
private String DeviceName;
private String DeviceDescription;
public String getDeviceName() {
return DeviceName;
}
public void setDeviceName(String deviceName) {
this.DeviceName = deviceName;
}
public String getDeviceDescription() {
return DeviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.DeviceDescription = deviceDescription;
}
}
The GET petition returns this JSON below:
[{"DeviceName":"AMIXT-20EC-VIDM0000","DeviceDescription":"Samsung device "},{"DeviceName":"AMIXT-E0F9-VIDM0001","DeviceDescription":"Tablet Huawei"}]
I've tried solutions like this one (also tried with getForObject):
ResponseEntity<Devices[]> responseEntity = restTemplate.getForEntity(url, Devices[].class);
As we can see in this photo, the body properties are null
What am I missing?
The issue is related to the name of the fields in the JSON, name start with an upper-case letter.
One simple solution would be to use #JsonProperty annotation on the variables defined in the Devices class
public class Devices {
#JsonProperty("DeviceName")
private String deviceName;
#JsonProperty("DeviceDescription")
private String deviceDescription;
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
}

GSON deserialize POJO with root value included

I would like to achieve the opposite of this:
Gson serialize POJO with root value included?
I get a JSON String
{"RootDTO":{"classField_01":"value"}}
and would like to deserialise that to
class RootDTO {
String classField_01;
//nice getter and setter are included
}
I know how to set the parameter so that in serialisation the root element is included into the JSON string.
But how to deserialise that now? Searching like hell already but wasn't able to find a suitable answer.
Thank you for any ideas!
make RootDTO.java
import com.google.gson.annotations.SerializedName;
public class RootDTO {
#SerializedName("classField_01")
private String classField_01;
public String getClassField_01() {
return classField_01;
}
public void setClassField_01(String classField_01) {
this.classField_01 = classField_01;
}
}
make Response.java
import com.google.gson.annotations.SerializedName;
public class Response {
#SerializedName("RootDTO")
private RootDTO rootDTO;
public RootDTO getRootDTO() {
return rootDTO;
}
public void setRootDTO(RootDTO rootDTO) {
this.rootDTO = rootDTO;
}
}
test this code to generate your json data
import com.google.gson.Gson;
public class TestOne {
public static void main(String[] args) {
RootDTO dto = new RootDTO();
dto.setClassField_01("value");
Response response = new Response();
response.setRootDTO(dto);
String result = (new Gson()).toJson(response);
System.out.println(""+result);
}
}
output generated with this code
{"RootDTO":{"classField_01":"value"}}

Hide JSON fields in Jersey RESTful

The thing is that I want to hide the null elements from a RESTFul JSON response (if it's possible).
The REST controller retrieves the information from a Mongo database and because this elements doesn't exist there I would like to ignore them when they are null.
This is my REST Controller (exposed with Jersey):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#Path(PropertiesRestURIConstants.PROPERTIES)
#Produces(MediaType.APPLICATION_JSON)
#RequestScoped
public class GetPropertiesController {
#EJB(mappedName = PropertiesManagerRemote.MAPPED_NAME)
PropertiesManagerRemote propertiesManager;
#GET
#Path(PropertiesRestURIConstants.PROPERTIES_ALL)
public List<PropertyEntity> getAllProperties() throws DBLayerException {
return propertiesManager.getAllProperties();
}
...
...
...
}
This is my entity:
#Document(collection = "property")
public class PropertyEntity implements GenericEntity {
#Id
private String id;
private String propertyName;
private String propertyValue;
public PropertyEntity() {
}
public PropertyEntity(String propertyName, String propertyValue) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
...
...
...
}
And this is the result:
[{"id":"542c00c2ff5e0ba4ea58790d","propertyName":"property1","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790e","propertyName":"property2","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790f","propertyName":"property3","propertyValue":null}]
I use Spring Data for the persistence layer. I tried with JSONIgnore annotations and similar things, but nothing works for me.
Any help will be welcome.
Thanks in advance.
Try to annotate it this way:
#JsonInclude(Include.NON_EMPTY)
public class PropertyEntity implements GenericEntity {

Jackson deserialization - with contained ArrayList<T>

Good day,
I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:
{
"error":null,
"object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}
I have two classes to represent the type (the outer ListResponse):
public class ListResponse {
public String error;
public ArrayList<ListItem> object;
public ListResponse() {
}
}
and (the inner ListItem):
public class ListItem {
#JsonProperty("Id")
public int id;
#JsonProperty("Class")
public String classType;
#JsonProperty("ClassID")
public String classId;
#JsonProperty("ListItemParent_ID")
public int parentId;
#JsonProperty("Item")
public String item;
#JsonProperty("Description")
public String description;
#JsonAnySetter
public void handleUnknown(String key, Object value) {}
public ListItem() {
}
}
The class that invokes and returns the JSON looks like this:
public class CitizenPlusService {
private Client client = null;
private WebResource service = null;
public CitizenPlusService() {
initializeService("http://localhost:59105/PlusService/");
}
private void initializeService(String baseURI) {
// Use the default client configuration.
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
// Add a logging filter to track communication between server and client.
client.addFilter(new LoggingFilter());
// Add the base URI
service = client.resource(UriBuilder.fromUri(baseURI).build());
}
public ListResponse getListItems(String id) throws Exception
{
ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);
return response;
}
}
The important call here is the getListItems method. Running the code in a test harness, produces the following:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader#49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])
Please assist.
Regards,
Carl-Peter Meyer
You may be missing a #JsonDeserialize attribute as the type information does get lost in generics at run-time. Also you should avoid using concrete classes for collections if you can.
public class ListResponse {
public String error;
#JsonDeserialize(as=ArrayList.class, contentAs=ListItem.class)
public List<ListItem> object;
}
Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).
I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:
public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
private final BeanProperty property;
/**
* Default constructor needed by Jackson to be able to call 'createContextual'.
* Beware, that the object created here will cause a NPE when used for deserializing!
*/
public JsonCollectionDeserializer() {
super(Collection.class);
this.property = null;
}
/**
* Constructor for the actual object to be used for deserializing.
*
* #param property this is the property/field which is to be serialized
*/
private JsonCollectionDeserializer(BeanProperty property) {
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new JsonCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
switch (jp.getCurrentToken()) {
case VALUE_STRING:
// value is a string but we want it to be something else: unescape the string and convert it
return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
default:
// continue as normal: find the correct deserializer for the type and call it
return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
}
}
}
Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.
In your example you would now have to annotate your collection field like so:
public class ListResponse {
public String error;
#JsonDeserialize(using = JsonCollectionDeserializer.class)
public ArrayList<ListItem> object;
public ListResponse() {}
}
And that should be it.
Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.
The register subTypes works!
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
}
public class Point implements Geometry{
private String type="Point";
....
}
public class Polygon implements Geometry{
private String type="Polygon";
....
}
public class LineString implements Geometry{
private String type="LineString";
....
}
GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
try {
geojson=mapper.readValue(source, GeoJson.class);
} catch (IOException e) {
e.printStackTrace();
}