How to parse inner json string as nested class using retrofit - java

I have used retrofit with nested classes before, but the current api I'm tring to use has such a structure:
Request body:
{
"Id" : "a2",
"messageCode" : 1,
"bigNestedClass" : "{\"field1\":238,\"otherField\":246,\"ip\":\"10.255.130.154\",\"someOtherField\":15,\"Info\":1501069568}"
}
and a similar response body.
Note that bigNestedClass is a string.
I created different pojo classes for request and response. However, creating a nested BigNestedClass makes this field filled as JSON object, as expected, not a JSON string. And I have same problem parsing the response too.
My question: Is there a way in retrofit that enables encode, parse nested classes as strings?
I use Retrofit 2.0
I use gson (can be changed)

With gson I would simply make this with TypeAdapter. See the class below:
public class MyTypeAdapter extends TypeAdapter<BigNestedClass> {
private Gson gson = new Gson();
#Override
public BigNestedClass read(JsonReader arg0) throws IOException {
// Get the string value and do kind of nested deserializing to an instance of
// BigNestedClass
return gson.fromJson(arg0.nextString(), BigNestedClass.class);
}
#Override
public void write(JsonWriter arg0, BigNestedClass arg1) throws IOException {
// Get the instance value and insted of normal serializing make the written
// value to be a string having escaped json
arg0.value(gson.toJson(arg1));
}
}
Then you would only need to register MyTypeAdapter with gson, like:
private Gson gson = new GsonBuilder()
.registerTypeAdapter(BigNestedClass.class, new MyTypeAdapter())
.create();
To have retrofit to use it you would need to do just a bit more when creating it:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

Related

Convert multiple Java Beans to JSON

I have multiple Java bean classes that are associated to each other (JSON Array + JSON Object) since they have a nested structure.
There are about 10 classes. Is there a way to collectively convert these classes or at-least one by one?
I had created these classes out of a JSON data which I don't have access to right now.
So, now, what I'm looking forward is to create a dummy JSON out of those classes.
Using GSON, I tried converting one of these Bean classes however, I got an empty result. Here is one of the beans called Attachment.java.
Attachment.java
package mypackagename;
import java.io.Serializable;
public class Attachment implements Serializable{
private Payload payload;
private String type;
public Payload getPayload() {
return payload;
}
public void setPayload(Payload payload) {
this.payload = payload;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Implementation
Gson gson = new Gson();
Attachment attachment = new Attachment();
String json = gson.toJson(attachment);
Sure you got an empty result. Because your JSON object is empty. You should add data to your object and test it again as below:
Attachment attachment = new Attachment(new Payload("Test Payload"), "Test attachment");
String json = new Gson().toJson(attachment);
Log.e("Test", "Json: " + json); // result: Json: {"payload":{"test":"Test Payload"},"type":"Test attachment"}
To avoid empty object, you have to set a default value to your payload and type becaus Gson will ignore any null value.
This section of the Gson User Guide: https://sites.google.com/site/gson/gson-user-guide#TOC-Finer-Points-with-Objects
The fourth bullet point explains how null fields are handled.

How to handle a response of a service returning either a single object or an array of these?

I am trying to invoke a third-party API through REST call in Spring. Currently, I'm using postForObject. I am converting the request class to string and calling the post for object. The response is taken as string and then converted it into the class. I have defined the class with below parameters
Class responseDto {
private Arraylist < Response > response;
getResponse();
setResponse();
}
Response {
String code;
String trid;
Getters();
Setters();
}
I am using Jackson dependency to serialize and deserialize. This class is working fine for the below response:
{
"response":[
{
"code":"100",
"trid":"123"
}
]
}
However, in error scenario, the request returns a JSON class with the same name 'response' as given below
{
"response":{
"code":"700",
"trid":"123"
}
}
The deserialize fails for the class I defined with some JSON mapping exception:
com.fasterxml.jackson.databind.JsonMappingException: Can not
deserialize instance of java.util.ArrayList out of START_OBJECT token
How can I resolve this issue in Java and Spring?
SOLUTION 1: Using #JsonFormat ( > 2.6 version)
Just annotate your field with #JsonFormat as
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Feature;
public class ResponseDto {
#JsonFormat(with = Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Response> response;
public List<Response> getResponse() {
return response;
}
public void setResponse(List<Response> response) {
this.response = response;
}
}
SOLUTION 2: Setting DeserializationFeature
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
// global setting, can be overridden using #JsonFormat in beans
// when using #JsonFormat on fields, then this is not needed
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
ResponseDto dto = mapper.readValue(stringResponse, ResponseDto.class);
}
Now response node in json containing single object, single object array, multiple object array will be successfully parsed as list of Response object.

POST JSON with JSON attribute to java API rest

I have a java API (rest) that accepts a JSON as body request.
I need to map the body to a java class, but one of the JSON attributes is a JSON itself, and I cannot predict its structure.
For example, this is the resource:
#POST
public void paymentInfo(PaymentInfoEntity paymentInfoEntity)
throws JsonGenerationException, JsonMappingException, IOException {
...
}
This is a JSON example:
{
progressive: 123,
params: { [another valid JSON node] }
}
This is how I'd like the entity:
public class PaymentInfoEntity {
public Integer progressive;
public JsonNode params;
}
With this configuration, I have this error:
org.codehaus.jackson.map.JsonMappingException: Can not construct
instance of com.fasterxml.jackson.databind.JsonNode, problem: abstract
types can only be instantiated with additional type information
The same error occour if I declare BaseJsonNode params instead of JsonNode params.
If I declare, instead, a Object params, it works.
How can I then parse a JSON from this? In this case params.toString() is the form {key1=stringvalue1, key2=stringvalue2} instead of {key1: "stringvalue1", key2: "stringvalue2"}.
Which is the best option to achieve my goal?
Thank you!
JsonNode is an abstract type. Try to use BaseJsonNode
or java.util.Map

How to use Gson to parse multiple types of payloads

I am using GSON parser to parse Http Response with JSON format as:
{ "type" : "A1", "payload": <Format as per type A1> }
{ "type" : "A2", "payload": <Format as per type A2> }
.
.
.
I don't have control over JSON output as I am writting only http client
I have defined base class as:
class Base {
String type;
Object payload;
}
Gson g = new Gson();
Base baseObj = gson.fromJson(response, Base.class);
// Need to cast and access baseObj.payload to specific class
But now I want to cast "Object payload" to specific class and access its member variables
well for nested objects in a JSON response you don't control of, you have to write a deserializer. See for GSON specific implementation: https://stackoverflow.com/a/23071080/6471273.
In theory, I don't really recommend this, because it's hackish, but you could also make it work depending if its throw-away code by doing something simple like below.
class Baselike {
String type;
String payloadStr;
}
class Payload
{
public int foo; // whatever the format is
public String bar;
}
Gson g = new Gson();
Baselike baseObj = gson.fromJson(response, Baselike.class);
Payload payloadObj = gson.fromJson(baseObj.payloadStr, Payload.class);
You can use jackson to deserialize your json to class.
You can write something like this:
ObjectMapper objectMapper = new ObjectMapper();
SampleClass abc= objectMapper.readValue(jsonData, SampleClass .class);

GSON treat part of JSON as String [duplicate]

I am trying to deserialize a json object into a java bean.
The main issue I am facing is that I'd like to treat the field object of the json string as a plain string, even if it contains a potentially correct json object.
The json structure is like this:
{
"type":"user",
"object":{
"id":"1",
...}
}
How can i tell gson to ignore the object value so that it doesn't get deserialized into an object? I'd like it only to be mapped to a plain String field in my bean so that I can dispose a proper deserialization for it, once I got the type from the type field.
Just declare it as of type JsonObject
class ExampleJsonModel {
#SerializedName("type")
public String type;
#SerializedName("object")
public JsonObject object;
}
I don't know if your problem is solved. I ran into a similar question and here it is how I worked it out:
JsonDeserializer allows you to make you own adapter to deserialize that **:
class JavaBeanDeserializer implements JsonDeserializer<JavaBeanObject>() {
public JavaBeanObject fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// return JavaBeanObject built using your logic.
}
You've to register JavaBeanDeserializer to Gson object when building it:
Gson gson = new GsonBuilder().registerTypeAdapter(JavaBeanObject.class, new JavaBeanDeserializer()).create();

Categories

Resources