I am facing a problem while parsing API response using Retrofit 2.
The API's are already in production and I cannot request a change in API.
Following are two different responses I am getting from server
Success response:
{
"status":0,
"empId":121,
"message":"Data available",
"data":{
"name":"Sam",
"designation": "Software Engineer",
"mob": "1255565456"
}
}
Failure response
{
"status":10,
"empId":121,
"message":"No data available",
"data":""
}
Parsing Classes
class Response{
public int status;
public String message;
public int empId;
public Student data;
}
class Student{
public String name;
public String designation;
public String mob;
}
I am able to parse the success response. But getting the following exception for the failure case.
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
try this ,
Object getrow = null;
try {
getrow = this.// your object
LinkedTreeMap<Object, Object> tree = (LinkedTreeMap) getrow;
String name = tree.get(<your body>).toString()
} catch (Exception e) {
e.printStackTrace();
}
The way that you can handle this situation is by treating 'data' as a generic object rather than as a String or 'Student'.
p̶u̶b̶l̶i̶c̶ ̶S̶t̶u̶d̶e̶n̶t̶ ̶d̶a̶t̶a̶;̶
public Object data;
While Using data add a check like this
if(data instanceof String){
String parsedData=data.toString();
}else{
Student parsedData= (Student) data;
}
Make Student as an inner class or Response class. and Retrofit will parse the response and will give you the object.
class Response
{
public int status;
public String message;
public int empId;
public Student data;
Class Data
{
public String name;
public String designation;
public String mob;
}
}
Related
I am using Quarkus to receive messages from Kafka.
When I use the method with just one class, deserialization happens normally.
When my class is subclassed, I can't continue with the deserialization and an error occurs.
My Input in console kafka:
{"id":"73707ad2-0732-4592-b7e2-79b07c745e45","currentstep":"Debit-approval","payload": "{\"idCard\": 2,\"balance\": 456,\"pin\":222}","sagastatus": "STARTED","stepstatus": "{\"credit-approval\":\"STARTED\"}","type":"order-placement","version": 1}
My Method.
#Incoming("process-transaction-in")
public void process(TransactionModel transaction) throws InterruptedException { }
my deserialize class
import io.quarkus.kafka.client.serialization.ObjectMapperDeserializer;
public class TransactionDeserializer extends ObjectMapperDeserializer<TransactionModel> {
public TransactionDeserializer() {
super(TransactionModel.class);
}
My class Model
public class TransactionModel {
public TransactionModel(String id,
String currentStep,
PayloadModel payload,
String sagaStatus,
String stepStatus,
String type,
String version) {
this.id = id;
this.currentStep = currentStep;
this.payload = payload;
this.sagaStatus = sagaStatus;
this.stepStatus = stepStatus;
this.type = type;
this.version = version;
}
public String id;
public String currentStep;
public PayloadModel payload;
public String sagaStatus;
public String stepStatus;
public String type;
public String version;
public TransactionModel() {
payload = new PayloadModel();
}
}
}
The Class PayloadModel
public class PayloadModel {
public PayloadModel(String idCard,
String current,
String pin)
{
this.idCard = idCard;
this.current = current;
this.pin = pin;
}
public String idCard;
public String current;
public String pin;
public PayloadModel() {}
}
Error:
SRMSG18249: Unable to recover from the deserialization failure (topic: process-transaction), configure a DeserializationFailureHandler to recover from errors.: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of payment.model.PayloadModel (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"idCard": 2,"balance": 456,"pin":222}')
I followed the following tutorial: https://quarkus.io/guides/kafka#kafka-serialization
Has anyone experienced this problem?
As you can see in the error log Jackson finds at least one constructor for PayloadModel class but it's not the one it's expecting since the "payload" parameter in your Kafka payload is a string and not a JSON Object. Try to change the way you serialize your data so that payload is serialized as an object.
Sorry for posting this as a response I don't have enough reputation to comment.
I have a POJO which is used for mapping the values received from rabbitmq. The messages are sent from other third-party services which I don't have control over. The problem is that these services don't send the certain fields in a consistent manner.
For example, in the case of an error field, this field will only have information when the third-party service wants to send a message to inform that there is an error. Some services may send in their JSON message to the queue this way:
{
id: 123,
name: "PersonA",
error: null
}
And then there are also services that may send their JSON message this way:
{
id: 123,
name: "PersonA",
error: []
}
And there are also services that may just omit the field entirely:
{
id: 123,
name: "PersonA"
}
Of course, if there is an error, that error field will contain fields within it:
{
id: 123,
name: "PersonA",
error: {
message: "Cannot find PersonA"
}
}
So, there is a chance that the error field can be null, undefined, array or an object.
The problem is, I can only define a single type in my POJO for the error field.
public class MyMessage {
private int id;
private MessageError error;
private String name;
#JsonCreator
public MyMessage(
#JsonProperty("id") int id,
#JsonProperty("error") MessageError error, // error is a MessageError object already but what is sent in could be null, undefined, an array or an object
#JsonProperty("name") String name
) {
this.id = id;
this.error = error;
this.name = name;
}
}
public class MessageError {
private String message;
#JsonCreator
public MessageError(
#JsonProperty("message") String message
) {
this.message = message;
}
}
I'm using Spring with Jackson.
In this case, how can I handle and map all the possible values that maybe assigned to the error field in the message to the POJO when it gets deserialised?
If you don't have any markers in the json data to identify its source and thus its shape, I would try to set error field type to object, and if it doesn't work or fit, to "json raw" as explained in answers of How can I include raw JSON in an object using Jackson?
So you can proceed the error field in the appropriate way according to the content you would discover at runtime, peeking from expected ones you detailed above.
The simplest way to resolve this problem is to define error field as a common Object type
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"id",
"name",
"error"
})
public class MyMessage {
#JsonProperty("id")
private int id;
#JsonProperty("name")
private String name;
#JsonProperty("error")
private Object error;
#JsonProperty("id")
public int getId() {
return id;
}
#JsonProperty("id")
public void setId(int id) {
this.id = id;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("error")
public Object getError() {
return error;
}
#JsonProperty("error")
public void setError(Object error) {
this.error = error;
}
}
And then you have to map it to another DTO:
// someplace where you will map it
MyMessage message = source.getMessageFromQueue();
if(message.getError() == null) {
// do something
} else {
Object error = message.getError();
handleError(error);
}
private void handleError(Object error) {
if(error instanceOf MessageError) {
handleMessageError((MessageError) error) // handle as known MessageError object
} else if(error instanceOf String) {
handleStringError((String) error) // handle as String, write in logs maybe
} else if(error instanceOf List) {
handleErrors((String) error) // handle as List errors
} else {
handleUndefinedError()
}
}
I am developing a WEb Service Rest in java, netbeans.
This is the JSON I want to receive:
{
"ticket":"2132158645161654561651616",
"avaliacoes":[
{
"id":1,
"nome":"Atendimento",
"nota":5,
"observacoes":"testeTEste"
},
{
"id":2,
"nome":"Atendimento",
"nota":5,
"observacoes":"testeTEste"
}
]
}
Reception Class
#PUT
#Consumes(MediaType.APPLICATION_JSON)
#Path("venda/enviardados")
public String postVenda(#QueryParam("key") String key, #QueryParam("serial") String serial, VendaAvaliacao va) {
...
}
Entity Classes
public class VendaAvaliacao {
private int id;
private String ticket;
//private List<VendaAvaliacaoInner> avaliacoes = new ArrayList<>(); //I've tried it too
private VendaAvaliacaoInner[] teste;
}
public class VendaAvaliacaoInner {
private int id;
private String nome;
private int nota;
private String observacao;
}
The ticket is received and populated, but array = null.
I've read other similar topics but they did not help .... how can I do?
https://pt.stackoverflow.com/questions/6046/convers%C3%A3o-de-string-json-para-objeto-java?rq=1
https://pt.stackoverflow.com/questions/159725/receber-valor-de-array-json-para-string-java
https://pt.stackoverflow.com/questions/290759/como-obter-objetos-de-um-array-de-json-usando-jsonarray-no-java
Maybe the service REST you are consuming don't populate the data in the array.
As an advice i would work with List<T> rather than T[]
Also i see that you are mapping 3 properties in you object but the id property don't exist in raw JSON isn't it ?
The GET request to tenants/client/{hostname} returns a json object with the property baseApiHostname.
#GET("api/tenants/client/{hostname}")
Call<HostName> getHostname(#Path("hostname") String hostname);
baseApiHostname is successful retrieved. That hostname which is used to create the rest of tenant requests from.
Log.v("base url","testing "+preferenceManager.getTenantHostname());
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(builder.create()))
.baseUrl(preferenceManager.getTenantHostname())
.build();
I received the error "java.lang.IllegalArgumentException: Illegal URL".
stacktrace message
After attaching debugger I realized that hostname = null.
debugger message
Call<HostName> call = hostNameApiService.getHostname(tenant);
call.enqueue(new Callback<HostName>() {
#Override
public void onResponse(Call<HostName> call, Response<HostName> response) {
if (response.isSuccessful()) {
String hostname = response.body().getBaseApiHostname();
preferenceManager.setTenantHostname(hostname);
retrieveCampaign();
} else {
Toast.makeText(StartSurveyActivity.this, "Error Retrieving Hostname", Toast.LENGTH_SHORT).show();
}
}
Here is the Hostname model class
#SerializedName("id")
private String id;
#SerializedName("name")
private String name;
#SerializedName("baseApiHostName")
private String baseApiHostname;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBaseApiHostname() {
return baseApiHostname;
}
public void setBaseApiHostname(String baseApiHostname) {
this.baseApiHostname = baseApiHostname;
}
If there's any other detail required that will aid in helping myself and other developers who may have faced a similar problem, please don't hesistate to ask.
Thanks in advance guys.
GSON is not successfully serializing any response to the field you're retrieving, so there is some misalign in how you are setting up gson to parse your json result.
Given your object and your sample JSON, your field in JSON is "baseApiHostname" whereas in your object your GSON annotation is for serialized name "baseApiHostName".
You are setting null in your Retrofit.Builder for your base URL. What is the URL for your server? You need to set that for the first time. Then you can change it after that if need be but it can't be null the first time.
I'm new to Java. I'm using Spring to consume a REST api that outputs JSON. With the tutorials on the Spring website I can easily have the JSON response converted to an object of my desired class. The problem is now that one of the keys in the JSON response is $id. I cannot make a variable with a dollar sign in it. I assume I should define some configuration somewhere that such a name would be converted into something acceptable. I don't know how.
My Rest request code:
protected LoginResult doInBackground(Void... params) {
try {
Log.i(TAG, "Making Login request");
//TODO: Make this a setting
final String url = "https://someurl.com/api/login";
LoginCredentials login = new LoginCredentials("foo#bar.com", "qwerty123");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
LoginResult result = restTemplate.postForObject(url, login, LoginResult.class);
Log.d(TAG, "Got the LoginResult.");
return result;
} catch (Exception e) {
//TODO: Exception handling
Log.e(TAG, e.getMessage(), e);
}
return null;
}
The resulting JSON looks something like this:
{
"_id":{
"$id":"98765432"
},
"name":"Person Guy",
"email":"foo#bar.com",
"roles":[
"user"
],
"active":true,
"created":{
"sec":1439117849,
"usec":856000
},
"session":{
"token":"12345678",
"user_id":"98765432",
"created":{
"sec":1439134272,
"usec":0
},
"last_extended":{
"sec":1439134272,
"usec":0
},
"expires":{
"sec":1439998272,
"usec":0
}
}
}
The $id part is where things get difficult. The LoginResult class looks like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class LoginResult {
private String name;
private String email;
private MongoId _id;
/* Getters and setters */
}
The MongoId class looks like this (The JsonIgnoreProperties is now added to avoid exceptions):
#JsonIgnoreProperties(ignoreUnknown = true)
public class MongoId {
private String id; //This is $id in the JSON response.
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Any help would be largely appreciated.
You can use the #JsonProperty("$id") annotation in MongoId to tell how the JSON is mapped to your Java object:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MongoId {
#JsonProperty("$id")
private String id; //This is $id in the JSON response.
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Here is a quick overview for reference.