Error when using #JsonDeserialize with #JsonCreator - json

I have a class which I'm trying to deserialise some JSON into. One of the properties has a custom converter attached via the #JsonDeserialize annotation. The deserialisation works fine when the class is written with setters and the annotations on the setters like this:
public class Rates extends AbstractResponse {
private Date effectiveDate;
private List<Rate> rates;
public Rates(Date effectiveDate, List<Rate> rates) {
this.effectiveDate = effectiveDate;
this.rates = rates;
}
public Date getEffectiveDate() {
return this.effectiveDate;
}
#JsonProperty("timestamp")
public void setEffectiveDate(Date effectiveDate) {
this.effectiveDate = effectiveDate;
}
#JsonDeserialize(converter = RatesTableConverter.class)
public void setRates(List<Rate> rates) {
this.rates = rates;
}
public List<Rate> getRates() {
return this.rates;
}
}
But when I re-write it to be like this:
public class Rates extends AbstractResponse {
private final Date effectiveDate;
private final List<Rate> rates;
#JsonCreator
public Rates(
#JsonProperty("timestamp") Date effectiveDate,
#JsonProperty("rates")
#JsonDeserialize(converter = RatesTableConverter.class) List<Rate> rates) {
this.effectiveDate = effectiveDate;
this.rates = rates;
}
public Date getEffectiveDate() {
return this.effectiveDate;
}
public List<Rate> getRates() {
return this.rates;
}
}
I get this error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
I've messed around with a number of ideas and cannot get it to work. It appears that when used on a constructor, that the deserialiser is not being checked correctly.
Any ideas what I've missed?

Related

SpringBoot JSON not deserializing into my request model

I am using SpringBoot and trying to deserialize JSON like:
{
"userId": "Dave",
"queryResults": {
"id": "ABC",
"carData": {.....},
"carId": "Honda",
"status": 0,
"model": "X"
}
}
, into MyRequestModel clas:
public class MyRequestModel {
private String userId;
private String: queryResults;
}
, that is received as #RequestBody parameter in my #PostMapping method that looks like:
#PostMapping
public String postDate(#RequestBody MyRequestModel data) {
...
return "posted";
}
The above queryResults field is supposed to be stored as a CLOB in a database.
Problem I am having is that if I send this JSON to hit my endpoint (PostMapping) method, it cannot deserialize it into MyRequestModel and I get this error:
Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (PushbackInputStream); line: 3, column: 18] (through reference chain: MyRequestModel["queryResults"])]
I guess the real answer to your question is: if you NEED the queryResults property to be a String, then implement a custom deserializer.
If not, then, use one of the alternatives that Jonatan and Montaser proposed in the other answers.
Implementing a custom deserializer within Spring Boot is fairly straightforward, since Jackson is its default serializer / deserializer and it provides a easy way to write our own deserializer.
First, create a class that implements the StdDeserializer<T>:
MyRequestModelDeserializer.java
public class MyRequestModelDeserializer extends StdDeserializer<MyRequestModel> {
public MyRequestModelDeserializer() {
this(null);
}
public MyRequestModelDeserializer(Class<?> vc) {
super(vc);
}
#Override
public MyRequestModel deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String userId = node.get("userId").asText();
String queryResults = node.get("queryResults").toString();
MyRequestModel model = new MyRequestModel();
model.setQueryResults(queryResults);
model.setUserId(userId);
return model;
}
}
Second, mark your class to be deserialized using your custom deserializer by using the #JsonDeserialize annotation:
MyRequestModel.java
#JsonDeserialize(using = MyRequestModelDeserializer.class)
public class MyRequestModel {
private String userId;
private String queryResults;
}
It's done.
queryResults is a String on Java side but it is an Object on JSON side.
You will be able to deserialize it if you send it in as a String:
{
"userId": "Dave",
"queryResults": "foo"
}
or if you create classes that maps to the fields:
public class MyRequestModel {
private String userId;
private QueryResults queryResults;
}
public class QueryResults {
private String id;
private CarData carData;
private String carId;
private Integer status;
private String model;
}
or if you serialize it into something generic (not recommended):
public class MyRequestModel {
private String userId;
private Object queryResults;
}
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
public class MyRequestModel {
private String userId;
private JsonNode queryResults;
}
You have two options to deserialize this request:-
change the type of queryResults to Map<String, Object>, it will accepts everything as an object of key and value. (Not recommended)
public class MyRequestModel {
private String userId;
private Map<String, Object> queryResults;
}
You have to create a class that wraps the results of queryResults as an object.
class QueryResult {
private String id;
private Map<String, Object> carData;
private String carId;
private Integer status;
private String model;
public QueryResult() {}
public QueryResult(String id, Map<String, Object> carData, String carId, Integer status, String model) {
this.id = id;
this.carData = carData;
this.carId = carId;
this.status = status;
this.model = model;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getCarData() {
return carData;
}
public void setCarData(Map<String, Object> carData) {
this.carData = carData;
}
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
and make the type of queryResult as shown:-
public class MyRequestModel {
private String userId;
private QueryResult queryResults;
}

XML to JSON | Spring boot | Jackson

I am working on a Springboot project and fetching data from Rest API. The response is in XML format and I am facing difficulty while converting it to JSON
XML Response:
<StoreInfo>
<Store Number="1" NCPDPID="0411"/>
<Store Number="3" NCPDPID="1132"/>
<Store Number="4" NCPDPID="0407"/>
</StoreInfo>
The JSON classes I've created are:
public class IDResponse {
private List<IDInfo> StoreInfo;
}
public class IDInfo {
private List<Store> Store;
}
public class Store {
private Integer Number;
private String ID;
}
Code to fetch and convert:
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
ResponseEntity<String> response = restTemplate.exchange(requestUrl,
HttpMethod.GET, entity,
String.class);
XmlMapper xmlMapper = new XmlMapper();
responseData = xmlMapper.readValue(response, IDInfo.class);
The exception I am getting:
Method threw
'com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException'
exception.
Unrecognized field "Store" (class
com.walmart.datamodel.location.IDResponse), not marked as ignorable
How to fix this?
The uppercase names shouldn't be used in the Java classes.
Number and Id are attributes so you should use Annotatiaons to guide jackson:
Root
#JacksonXmlRootElement(localName = "StoreInfo")
public class IDResponse {
#JacksonXmlProperty(localName = "Store")
#JacksonXmlCData
#JacksonXmlElementWrapper(useWrapping = false)
private List<Store> storeInfo;
public List<Store> getStoreInfo() {
return storeInfo;
}
public void setStoreInfo(List<Store> storeInfo) {
this.storeInfo = storeInfo;
}
}
Store Element
public class Store {
#JacksonXmlProperty(localName="Number")
private Integer number;
#JacksonXmlProperty(localName="NCPDPID")
private String id;
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
IDInfo-class isn't needed.

Macros dont working with POJOs (CLOSED)

i don't know what or if i'm doing it wrong, but the conversion from json to my POJOs just don't work. I'm with the dependencies updated. When i called the method "readJSONFromURL" my return always is the object with yours attributes with value NULL. All the libs are up to date. My Objects:
public class ListHall {
private List<Hall> Hall;
public void setHall(List<Hall> hall) {
this.Hall = hall;
}
public List<Hall> getHall() {
if (Hall == null) {
this.Hall = new ArrayList<Hall>();
}
return Hall;
}
}
public class Hall {
private String id;
private String name;
private Integer count;
//getter and setters here
}
My DataMappers:
data_mapper ListHall:ListHallMapper
data_mapper Hall:HallMapper
My method conversor:
public ListHall mapHall() throws IOException {
ListHallMapper returnListHallMapper = new ListHallMapper();
HallMapper hallMapper = new HallMapper();
DataMapper.createContext(Arrays.asList(returnListHallMapper,
hallMapper),
(DataMapper mapper) -> {
mapper.setReadKeyConversions(Arrays.asList(DataMapper.CONVERSION_CAMEL_TO_SNAKE));
});
return returnListHallMapper.readJSONFromURL("http://192.168.0.16:7080/integration/hall", ListHall.class,"/ListHall");
}
My output JSON:
{"ListHall":{"Hall":[{"id":"0","name":"Salão 1","count":"10"},{"id":"f6a495c1-be5e-4476-a362-5d42e572bfae","name":"Salão 3","count":"0"}]}}
My result:My LIB:
What i'm doing it wrong?

DAO MVC: why my class is NOT POJO

I use DAO MVC, and I after some googling I consider to store some variables as Enum in java and String in MySQL. So I create in Item.java (that will be persist into Item table) static initialization and static methods to convert Enum into String and vise versa.
But someone said me that after this static initialization and static methods my Item.java class became NOT POJO.
Question:
Why it became NOT POJO?
And if I'll make those methods not static Item.java class will be POJO?
EDITED: MY code:
package model;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class Order {
public enum OrderStatus {
NOT_REVIEWED,
APPROVED,
REJECTED,
RETURNED
}
// ==================
// = Transient =
// ==================
private static final Map<String, OrderStatus> convertStringToOrderStatusMap = new HashMap<String, OrderStatus>(3);
private static final Map<OrderStatus, String> convertOrderStatusToStringMap = new EnumMap<OrderStatus, String>(OrderStatus.class);
static {
convertStringToOrderStatusMap.put("not reviewed", OrderStatus.NOT_REVIEWED);
convertStringToOrderStatusMap.put("approved", OrderStatus.APPROVED);
convertStringToOrderStatusMap.put("rejected", OrderStatus.REJECTED);
convertStringToOrderStatusMap.put("returned", OrderStatus.RETURNED);
convertOrderStatusToStringMap.put(OrderStatus.NOT_REVIEWED, "not reviewed");
convertOrderStatusToStringMap.put(OrderStatus.APPROVED, "approved");
convertOrderStatusToStringMap.put(OrderStatus.REJECTED, "rejected");
convertOrderStatusToStringMap.put(OrderStatus.RETURNED, "returned");
}
// ==================
// = Attributes =
// ==================
private Integer orderId; //Primary key
private OrderStatus status;
private Integer reimbursement;
private String firstName;
private String secondName;
private String passportData;
private String pickUpDate;
private String dropOffDate;
//java.util.Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2011-05-18 16:29:31");
private String customerCell;
private String customerAddress;
// ==================
// = Foreign Keys =
// ==================
private User user;
private Car car;
// ==================
// = Public methods =
// ==================
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getStatus() {
return convertOrderStatusToString(status);
}
public void setStatus(OrderStatus status) {
this.status = status;
}
public Integer getReimbursement() {
return this.reimbursement;
}
public void setReimbursement(Integer value) {
this.reimbursement = value;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSecondName() {
return secondName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
public String getPassportData() {
return passportData;
}
public void setPassportData(String passportData) {
this.passportData = passportData;
}
public String getPickUpDate() {
return pickUpDate;
}
public void setPickUpDate(String pickUpDate) {
this.pickUpDate = pickUpDate;
}
public String getDropOffDate() {
return dropOffDate;
}
public void setDropOffDate(String dropOffDate) {
this.dropOffDate = dropOffDate;
}
public String getCustomerCell() {
return customerCell;
}
public void setCustomerCell(String customerCell) {
this.customerCell = customerCell;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public static OrderStatus converStringToOrderStatus(String status) {
return convertStringToOrderStatusMap.get(status);
}
public static String convertOrderStatusToString(OrderStatus status) {
return convertOrderStatusToStringMap.get(status);
}
}
Because a Plain Old Java Object only has data. Adding logic and methods means that it's no longer Plain Old Java Object.
That doesn't necessarily make it a bad thing, but you might be able to refactor the logic out into a class of it's own.
Lets ignore POJO.
What they mean is Service Oriented vs Domain Driven.
Service Oriented follows strict separation of behavior from state. They call POJOs data objects which are essentially glorified structs. Thus you would put the static methods in the Service. In fact you probably wouldn't even want the methods static as that is also against the service oriented approach (see dependency injection and evil singleton).
Domain Driven follows the idea of classic OOP (e.g. Rails Active Record) in which they do believe its OK to put behavior in their POJOs. Consequently because state + behavior are coupled there is only one implementation and thus static methods in the domain object are OK.
If your going the DAO route your most likely doing Service Oriented. My opinion is if your going to do the DAO POJO route you should use immutable objects (shameless plug) for those data objects.
Finally putting an inline enum into a class from my knowledge does not violate any definition of POJO. That being said you should know about #Enumerated since your using JPA.

Jackson in Spring: how to unmarshal a "generic" class w/o it thinking it's a LinkedHashMap?

So my entities look like this:
public class HappyClass<T>
{
private String id;
prviate int ver;
private Object obj;
public String getId()
{
return this.id;
}
public void setId( String id )
{
this.id = id;
}
public int getVer()
{
return this.ver;
}
public void setVer( int ver )
{
this.ver = ver;
}
#JsonTypeInfo( use = Id.NONE )
public T getObj()
{
return obj;
}
public void setObj( T obj )
{
this.obj = obj;
}
}
public class HappyGeneric
{
private String someStuff();
public String getSomeStuff()
{
return this.someStuff();
}
public void setSomeStuff( String someStuff )
{
this.someStuff = someStuff;
}
}
If I instantiate a class like this:
HappyClass<HappyGeneric> hc = new HappyClass<HappyGeneric>();
If I send it to Spring in a #ResponseBody it returns this:
{
"id" : "iamsomeid",
"ver" : 123,
"obj" : {
"someStuff" : "iamsomestuff"
}
}
However, when Spring and/or Jackson attempts to unmarshal the same JSON, it figures out that the main class is a HappyClass, however, the getObj() it unmarshals to a LinkedHashMap and not a HappyGeneric no matter what I seem to annotate it with.
Anybody have any ideas how I can force Jackson to unmarshal that generic to the original class?
Thanks!
EDIT: I'm aware I can call mapper.convertValue( blah.getObj(), HappyGeneric.class ) and get the object out that way-- I was hoping to get Spring to figure it out automatically (through annotations, for example).