Trouble serializing with to JSON in Java using Jackson - java

Good morning guys!
I have a JSON strings that looks like:
{
"StatusCode":0,
"Message":null,
"ExecutionTime":0,
"ResponseData":[
{"Name":"name1","SiteId":"1234","Type":"Type1","X":"1234567","Y":"123456"},
{"Name":"Name2","SiteId":"2134","Type":"Type2","X":"1234567","Y":"1234567"},
{"Name":"Name3","SiteId":"3241","Type":"Type3","X":"1234567","Y":"1234567"},
{"Name":"Name4","SiteId":"4123","Type":"Type4","X":"123456","Y":"123456"}
]
}
I want to create an object where I can retrieve the Xand Y values.
I've been trying to use Jackson to serialize the JSON string, without success. I've created two extra classes for Jackson to use. One class for the top layer, StatusCode, Message, ExecutionTime and ResponseData which looks like
public class PL {
private Long statusCode;
private String executionTime;
private String message;
private ResponseData responseData;
public PL(){
}
public void setStatusCode(Long statusCode){
this.statusCode = statusCode;
}
public Long getStatusCode(){
return this.statusCode;
}
public void setExecutionTime(String executionTime){
this.executionTime = executionTime;
}
public String getExecutionTime(){
return this.executionTime;
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
public void setResponseData(ResponseData responseData){
this.responseData = responseData;
}
public ResponseData getResponseData(){
return this.responseData;
}
}
Where ReponseData is returned as an object, and then I have another class for serializing ResponseData which looks like
public class ResponseData {
private String name;
private String siteId;
private String type;
private String x;
private String y;
public ResponseData(){
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setSiteId(String siteId){
this.siteId = siteId;
}
public String getSiteId(){
return this.siteId;
}
public void setType(String type){
this.type = type;
}
public String setType(){
return this.type;
}
public void setX(String x){
this.x = x;
}
public String getX(){
return this.x;
}
public void setY(String y){
this.y = y;
}
public String getY(){
return this.y;
}
}
I then create an ObjectMapper with
private final static ObjectMapper mapper = new ObjectMapper();
and try to so read the values with
ResponseData e = mapper.readValue(result.toString(), ResponseData.class);
and end up with the exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "StatusCode" (class MyClass.ResponseData), not marked as ignorable (5 known properties: "x", "y", "siteId", "name", "type"])
as if it can't parse the first entry, StatusMessage. Even if I remove the second class and only try to parse the first four entries where i return ResponseData as a String I still get the same exception.

To start with, in PL you should have a List<ResponseData> not a simple ResponseData attribute. As you can see, in the JSON, ResponseData is an array "ResponseData":[...] so it will be deserialized as a List. Each element of the list will be a ResponseData object as you defined it.
Then you have a case issue, you have upper cases in the JSON that you don't have in your class attributes. You can use the #JsonProperty (See API) annotation to overcome the problem, this way:
class PL {
#JsonProperty("StatusCode")
private Long statusCode;
#JsonProperty("ExecutionTime")
private String executionTime;
#JsonProperty("Message")
private String message;
#JsonProperty("ResponseData")
private List<ResponseData> responseDatas;
public PL(){
}
// getters/Setters
}
class ResponseData {
#JsonProperty("Name")
private String name;
#JsonProperty("SiteId")
private String siteId;
#JsonProperty("Type")
private String type;
#JsonProperty("X")
private String x;
#JsonProperty("Y")
private String y;
public ResponseData(){
}
// getters/Setters
}
Then read your JSON as a PL object, like this:
ObjectMapper mapper = new ObjectMapper();
PL pl = mapper.readValue(json, PL.class);
for(ResponseData rd : pl.getResponseDatas()) {
System.out.println(rd.getX());
System.out.println(rd.getY());
}
This outputs:
1234567
123456
1234567
1234567
1234567
1234567
123456
123456

It is fairly straightforward. Define your response structure using composition of classes. It is unfortunate to use capitalised fields in JSON, which out-of-the-box requires capitalised field names in the Java DTO. Still those can be easily mapped to conventional low-case names either by using the ACCEPT_CASE_INSENSITIVE_PROPERTIES modifier on the ObjectMapper or by annotating fields with corresponding names. I prefer a property on the ObjectMapper as it keeps the DTO independent of the serialisation code and this technique is used in the test below (the test is green):
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestDeserialization50386188 {
public static class Response {
public static class ResponseDataType {
public String name;
public String siteId;
public String type;
public long x;
public long y;
}
public int statusCode;
public String message;
public long executionTime;
public List<ResponseDataType> ResponseData = new ArrayList<>();
}
private static final String data = "{\"StatusCode\":0,\"Message\":null,\"ExecutionTime\":0,\"ResponseData\":[{\"Name\":\"name1\",\"SiteId\":\"1234\",\"Type\":\"Type1\",\"X\":\"1234567\",\"Y\":\"123456\"},{\"Name\":\"Name2\",\"SiteId\":\"2134\",\"Type\":\"Type2\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name3\",\"SiteId\":\"3241\",\"Type\":\"Type3\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name4\",\"SiteId\":\"4123\",\"Type\":\"Type4\",\"X\":\"123456\",\"Y\":\"123456\"}]}";
#Test
public void deserialize_response_withJackson_ok() throws IOException {
ObjectMapper mapper = new ObjectMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
Response response = mapper.readValue(data, Response.class);
assertEquals(4, response.ResponseData.size());
assertEquals(1234567, response.ResponseData.get(2).x);
assertEquals(1234567, response.ResponseData.get(2).y);
}
}
You fill find the project with the executable test on this dedicated GitHub repo.
The "Clean Code" book by Uncle Bob does not really recommend the overuse of getters and setters so common in Java for DTOs, which a Response class is. Still you can replace all public fields with getter/setter pairs if you like but the clarity will suffer with no obvious gain on quality.

Use List to receive arrays.
private Long statusCode;
private String executionTime;
private String message;
public List<ResponseDataType> ResponseData
and it will do everything automatically.

Related

Convert dynamic json to java object with dynamic string value. No key value json structure

I want to convert below JSON structure to java object, Annotation bases.
What will be the pojo java class structure?
{
"Data1":{
"Name":"abc",
"Number":2
}
}
Data1 can by any string-like if it coming as data1 first time, next time it can be like "xyz".
How can we convert it using fasterxml json annotations?
class Node {
public String name;
public int number
}
class ConvertedPojo {
public Map<String, Node> attributes;
}
Since Data1 can be any string you need a map which will store all different string as key and value as json object
Class structure will be :
public class Data1{
#JsonProperty("Name")
private String name;
#JsonProperty("Number")
private int number;
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
number = number;
}
}
public class Data {
#JsonProperty("Data1")
Object data1;
public Object getData1() {
return data1;
}
public void setData1(Object data1) {
this.data1 = data1;
}
}
Take care of the variable naming convention.
Code to test:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Data data1 = mapper.readValue("{\"Data1\":{\"Name\":\"abc\",\"Number\":2}}", Data.class);
System.out.println(mapper.writeValueAsString(data1));//{"Data1":{"Name":"abc","Number":2}}
Data data2 = mapper.readValue("{\"Data1\":\"data value\"}", Data.class);
System.out.println(mapper.writeValueAsString(data2));//{"Data1":"data value"}
}

Same named fields with different types in gson parsing

I have a RequestModel defined as
public class RequestModel
{
public class Footage
{
public String date;
public String retrievedAt;
public String videoFileName;
public String availableUntil;
public boolean isAvailable;
}
public class People
{
public String first;
public String last;
}
public static final int USER_BLOCKED = 0;
public static final int USER_ACTIVE = 1;
public static final int USER_WAIT_PIN = 2;
public String _id;
public String status;
public String submittedAt;
public Footage footage;
public People teacher;
public People student;
public ArrayList<MessageModel> messages = new ArrayList<MessageModel>();
public boolean isExpanded = false;
public RequestModel()
{
}
My MessageModel is defined as
public class MessageModel
{
public String _id;
public String statusMessage;
public String submittedAt;
public RequestModel request;
public String status;
public String timestamp;
public boolean isExpanded = false;
public MessageModel()
{
}
}
I have an api call that pulls a single "RequestModel" item. However the messages list in that api call has "request" as a String instead of "RequestModel" object.
Is there any way i can make it parse as a different name or omit it entirely to bypass exceptions causing because of different types.
Use the annotation #SerializedName("") before declaring member to give it a substitute name
ex,
if you json looks like this
{
name:"",
age:0,
items:[...]
}
but your model class have the fields,
class User{
String name;
int age;
Data userItems[];
}
The field userItems in model is named items in the json,
you need to use that annotation on the field:
class User{
String name;
int age;
#SerializedName("items")
Data userItems[];
}
this way GSON will map items to userItems.

Deserializing JSON-valued string

We are using Spring Boot to expose a REST endpoint which is called by a dumb client which delivers us the following:
{
"timestamp": "2016-08-16T14:30.000Z",
"data": "{\"amount\":1,\"product\":\"BASIC PRODUCT\"}"
}
We've created the following objects:
#JsonDeserialize(builder = Message.Builder.class)
public final class Message {
private final String timestamp;
private final Data data;
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
public Builder withTimestamp(final String timestamp) {...}
public Builder withData(final Data data) {...}
}
}
and
#JsonDeserialize(builder = Data.Builder.class)
public final class Data {
private final String product;
private final int amount;
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {...}
public Builder withAmount(final int amount) {...}
}
}
and exposed the endpoint as
#RequestMapping(consumes = "application/json", method = POST)
public ResponseEntity<?> receive(#RequestBody Message message) {
/// ...
}
but control doesn't even reach the receive method and fails with 400 BAD REQUEST. I believe this has to do with the fact that data is a JSON-valued string. Does Jackson provide any annotation that I can use to force the JSON-valued string to be deserialized as an instance of Data?
The key is in public Builder withData() method of Message.Builder.class to explicitly parse JSON-valued string to Data type. Change the method parameter to String instead of Data and call ObjectMapper().readValue(JSON-valued string, Data.class) to deserialize it into Data.
For example like this:
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = new ObjectMapper().readValue(jsonValue, Data.class);
this.data = data;
return this;
}
For the clarity sake here you are my whole POJOs:
Message:
public final class Message {
private final String timestamp;
private final Data data;
private Message(Builder builder){
this.timestamp = builder.timestamp;
this.data = builder.data;
}
public String getTimestamp() {...}
public Data getData() {...}
#JsonPOJOBuilder
public static final class Builder {
private String timestamp;
private Data data;
private static ObjectMapper mapper = new ObjectMapper();
public Builder withTimestamp(final String timestamp) {
this.timestamp = timestamp;
return this;
}
public Builder withData(final String jsonValue) throws JsonParseException, JsonMappingException, IOException {
Data data = mapper.readValue(jsonValue, Data.class);
this.data = data;
return this;
}
public Message build() {
return new Message(this);
}
} // Builder
}
Data:
public final class Data {
private final String product;
private final int amount;
private Data(Builder builder){
this.product = builder.product;
this.amount = builder.amount;
}
public String getProduct() {...}
public int getAmount() {...}
#JsonPOJOBuilder
public static final class Builder {
private String product;
private int amount;
public Builder withProduct(final String product) {
this.product = product;
return this;
}
public Builder withAmount(final int amount) {
this.amount = amount;
return this;
}
public Data build() {
return new Data(this);
}
} // Builder
}
Hope it helps.

parse json when value of one json becomes key of another json in java

I have two jsons like these
{
"clientId":"patientId",
"vendorId":"businessKey"
}
{
"patientId":"1234",
"businessKey":"abcd"
}
I have java POJOs created like these
public class Patient{
private String patientId;
private String businessKey;
public String getPatientId() {
return patientId;
}
public void setPatientId(String patientId) {
this.patientId = patientId;
}
public String getBusinessKey() {
return businessKey;
}
public void setBusinessKey(String businessKey) {
this.businessKey = businessKey;
}
}
public class Client {
private String clientId;
private String vendorId;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getVendorId() {
return vendorId;
}
public void setVendorId(String vendorId) {
this.vendorId = vendorId;
}
}
I am using Jackson's ObjectMapper to parse the JSON. What I want to achieve is first read the first JSON, get the value from that and then read the actual value from the second JSON.
Ex: I read the first JSON to getClientId - "patientId"
Then in the second JSON I should read getPatientId - 1234.
How do I achieve this programmatically. I dont want to clutter my code by adding lot many if else blocks. Is there any library that I could use?

LibGDX parsing standard json to an object

I have a very basic problem.
I read though the LibGDX documentation a few times regarding JSON and Google around for an answer but it still does't work..
Basically I'm pulling json from a server like such which works as:
{"id":1,"facebook_id":"23432232","json":"{\"json\":\"test\"}"}
I have a class like this:
public class ServerJson
{
public static final String NAME = "ServerJson";
private int id;
private String facebookID;
private String json;
public ServerJson(){}
public ServerJson(int id, String facebookID, String json)
{
this.id = id;
this.facebookID = facebookID;
this.json = json;
}
public int getId() {
return id;
}
public String getFacebookID() {
return facebookID;
}
public String getJson() {
return json;
}
When I try to parse the code, it doesn't work. I get null:
String resultString = httpResponse.getResultAsString(); //{"id":1,"facebook_id":"23432232","json":"{\"json\":\"test\"}"}
Json json = new Json();
ServerJson serverJson = json.fromJson(ServerJson.class, resultString);
log(serverJson.getFacebookID()); //<< Is null.
Make sure the fields of your object class match up with the fields of the json object.

Categories

Resources