I have the following POJO that can be serialized into bytes or json
.
public final class Message {
private final Data data;
private final Request request;
private final Response response;
public Message() {
this.data = new Data();
this.request = new Request();
this.response = new Response();
}
public Data getData() {
return data;
}
public Request getRequest() {
return request;
}
public Response getResponse() {
return response;
}
public Object query(String pointer) {
return toJson().query(pointer);
}
public byte[] toBytes() {
try {
return new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(this);
} catch (JsonProcessingException ex) {
throw new MessageException(ex);
}
}
public JSONObject toJson() {
try {
return new JSONObject(new ObjectMapper().writeValueAsString(this));
} catch (JsonProcessingException ex) {
throw new MessageException(ex);
}
}
@Override
public String toString() {
try {
return toString(0);
} catch (MessageException ex) {
throw new MessageException(ex);
}
}
public String toString(int indent) {
try {
return toJson().toString(indent);
} catch (MessageException ex) {
throw new MessageException(ex);
}
}
}
Reference Classes:
public class Data {
private final Map<String, Map<String, Object>> dataMap;
public Data() {
this.dataMap = new HashMap();
}
public Data addToSet(String name, String key, Object value) {
Map<String, Object> map = dataMap.get(name);
if (map == null) {
map = new HashMap();
}
map.put(key, value);
dataMap.put(name, map);
return this;
}
public Map<String, Map<String, Object>> getSets() {
return dataMap;
}
public Data updateSet(String name, String key, Object value) {
return Data.this.addToSet(name, key, value);
}
public Data removeFromSet(String name, String key) {
Map<String, Object> map = dataMap.get(name);
if (map == null) {
throw new MessageException("No such property '" + key + "' for set '" + name + "'");
}
map.remove(key);
return this;
}
public Map<String, Object> getSet(String name) {
return dataMap.get(name);
}
}
public class Request {
private String method;
private String resource;
private final Map<String, Object> body;
private final Map<String, String> headers;
private final Map<String, String[]> parameters;
public Request() {
this.body = new HashMap();
this.headers = new HashMap();
this.parameters = new HashMap();
}
public String getMethod() {
return Objects.toString(method, "");
}
public String getResource() {
return Objects.toString(resource, "");
}
public Map<String, Object> getBody() {
return body;
}
public Map<String, String> getHeaders() {
return headers;
}
public Map<String, String[]> getParameters() {
return parameters;
}
public String getHeader(String name) {
return headers.get(name);
}
public Request setBody(String payload) {
try {
this.body.putAll(new ObjectMapper().readValue(payload, new TypeReference<Map<String, Object>>() {
}));
return this;
} catch (JsonProcessingException ex) {
throw new MessageException(ex);
}
}
public Request setMethod(String name) {
this.method = name;
return this;
}
public Request setResource(String name) {
this.resource = name;
return this;
}
public Request setHeaders(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}
public Request setParameters(Map<String, String[]> parameters) {
this.parameters.putAll(parameters);
return this;
}
}
public class Response {
private String code;
private String data;
private String messageId;
private String timestamp;
private String description;
public Response() {
}
public String getCode() {
return Objects.toString(code, "");
}
public String getData() {
return Objects.toString(data, "");
}
public String getMessageId() {
return Objects.toString(messageId, "");
}
public String getTimestamp() {
return Objects.toString(timestamp, "");
}
public String getDescription() {
return Objects.toString(description, "");
}
public Response setCode(String code) {
this.code = code;
return this;
}
public Response setData(String data) {
this.data = data;
return this;
}
public Response setMessageId(String messageId) {
this.messageId = messageId;
return this;
}
public Response setTimestamp(String timestamp) {
this.timestamp = timestamp;
return this;
}
public Response setDescription(String description) {
this.description = description;
return this;
}
}
When serializing to json I get a valid string
{
"request": {
"headers": {},
"method": "",
"resource": "",
"body": {
"whatsapp": {
"conversationId": "39f09c41-1bd3-4e81-b829-babed3747d4b",
"name": "Dave",
"source": "+123456789098"
},
"payment": {
"product": "chocolate",
"amount": 1,
"method": "cashapp",
"msisdn": "123456789098",
"entity": "The Fudge Shop"
}
},
"parameters": {}
},
"data": {
"sets": {
"whatsapp": {
"provider": "clickatell",
"name": "Dave",
"destination": "123456789098",
"source": "123456789098",
"message": "Your payment of $1.00 received, your receipt.no is QWJ124XPA9."
},
"cashapp": {
"amount": 1,
"receiptNo": "QWJ124XPA9",
"name": "Dave Chapelle",
"msisdn": "123456789098"
}
}
},
"response": {
"code": "202",
"data": "",
"messageId": "20210623160202a647d32ee9ae477f9c90d8b1fbfd763a",
"description": "Processing Request",
"timestamp": "2021-06-23 16:02:02.408"
}
}
When I attempt to deserialize the json back to a pojo
Message output = new ObjectMapper().readValue(json.toString(), Message.class);
I get the error :
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
The error seems to be generated from the Request class when attempting to deserialize the Map<String, Object>
body:
How may I deserialize the Map correctly?
The solution that worked for me was using custom deserialization, @JsonDeserialize
annotation & JsonDeserializer
interface, in order to achieve the desired results.
Below is the solution:
public class Request {
private String method;
private String resource;
@JsonDeserialize(using = BodyDeserializer.class)
private final Map<String, Object> body;
private final Map<String, String> headers;
private final Map<String, String[]> parameters;
public Request() {
this.body = new HashMap();
this.headers = new HashMap();
this.parameters = new HashMap();
}
public String getMethod() {
return method;
}
public String getResource() {
return resource;
}
public Map<String, Object> getBody() {
return body;
}
public Map<String, String> getHeaders() {
return headers;
}
public Map<String, String[]> getParameters() {
return parameters;
}
public String getHeader(String name) {
return headers.get(name);
}
public Request setBody(Map<String, Object> body) {
this.body.putAll(body);
return this;
}
public Request setMethod(String name) {
this.method = name;
return this;
}
public Request setResource(String name) {
this.resource = name;
return this;
}
public Request setHeaders(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}
public Request setParameters(Map<String, String[]> parameters) {
this.parameters.putAll(parameters);
return this;
}
private static class BodyDeserializer extends JsonDeserializer<Map<String, Object>> {
@Override
public Map<String, Object> deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonDeserializer<Object> deserializer = dc.findRootValueDeserializer(dc.constructType(Map.class));
Map<String, Object> map = (Map<String, Object>) deserializer.deserialize(jp, dc);
return map;
}
}
}