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
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.
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.
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
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);
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();