I am using Retrofit to make a HTTP request which returns an array of object and I am getting the following errors:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
The response returned is expected to be like this:
[ {key1: "value1", key2: "value2"}, {key1: "value1", key2: "value2"}, ... ]
I have the following class, for serializing the data:
public class data {
private List<element> dataList;
public List<element> getElements() {
return dataList;
}
public class element {
#SerializedName("key1")
private String key1;
#SerializedName("key2")
private String key2;
// Getters and Setters
}
}
Please let me know if you have any ideas. Thanks
The error was actually in my implementation of Retrofit Callback. My implementation was expecting an object when it should be expecting an array in this case. Thanks everyone for the help.
Before
//*****MyData*****//
public class MyData {
private List<Data> dataList;
public List<Data> getElements() {
return dataList;
}
public class Data {
#SerializedName("key1")
private String key1;
#SerializedName("key2")
private String key2;
// Getters and Setters
}
}
//*****Callback Implementation*****//
public class MyDataCallback extends Callback {
public MyDataCallback(MyDataCallbackListener<MyData> myDataCallbackListener) {
super(myDataCallbackListener);
}
#Override
public void success(MyData data, Response response) {
if (myDataCallbackListener != null) {
myDataCallbackListener.onCallbackComplete(true, response, MyDataCallback.CALLBACK_SUCCESS_MESSAGE, data);
}
}
}
After
//*****Data*****//
public class Data {
#SerializedName("key1")
private String key1;
#SerializedName("key2")
private String key2;
// Getters and Setters
}
//*****Callback Implementation*****//
public class MyDataCallback extends Callback {
public MyDataCallback(MyDataCallbackListener<List<Data>> myDataCallbackListener) {
super(myDataCallbackListener);
}
#Override
public void success(List<Data> data, Response response) {
if (myDataCallbackListener != null) {
myDataCallbackListener.onCallbackComplete(true, response, MyDataCallback.CALLBACK_SUCCESS_MESSAGE, data);
}
}
}
As Dave mentioned in his comment, it does seem strange that you have recursion in the class that I am assuming is your response object. (your class "data" has a list of "data" objects).
I would suggest something a little more strait forward such as this:
public class ResponseObject {
private ArrayList<DataObject> mDataObjects;
public ArrayList<DataObject> getDataObjects() {
return mDataObjects;
}
private class DataObject {
private String key1;
private String key2;
public String getKey1() {
return key1;
}
public String getKey2() {
return key2;
}
}
}
or since you are local maybe you can buy Jake a beer :) From his photo, I would check Rouge Ales, 21 Amendment or my favorite last time I was in SF - Magnolia
It's not valid JSON to begin with an array. You need to instead return something like this:
{
dataList: [
{
key1: "value1",
key2: "value2"
},
{
key1: "value3",
key2: "value4"
}
]
}
Then you can use GSON to deserialize that into your data class.
Related
I am looking to serialize my class using gson but I would like to omit the hashmap name. Is this possible with gson?
I have tried writing my own TypeAdapter but the map name is still written as the parent object.
I have a class which looks like
public class myClass {
#Expose
public Long timestamp;
#Expose
public String id;
#Expose
public HashMap<String, someOtherClass> myMap = new HashMap<>();
#Override
public String toString() {
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
return gson.toJson(this);
}
}
current output :
{
"timestamp": 1517245340000,
"id": "01",
"myMap": {
"mapKey1": {
"otherClassId": "100", // works as expected
}
"mapKey2": {
"otherClassId": "101", // works as expected
}
}
}
What I am hoping to get :
{
"timestamp": 1517245340000,
"id": "01",
"mapKey1": {
"otherClassId": "100", // works as expected
},
"mapKey2": {
"otherClassId": "100", // works as expected
}
}
Write your own TypeAdapter. See javadoc for example.
Specify it with #JsonAdapter annotation, or register it with GsonBuilder.
#JsonAdapter(MyClassAdapter.class)
public class MyClass {
public Long timestamp;
public String id;
public HashMap<String, SomeOtherClass> myMap = new HashMap<>();
}
public class MyClassAdapter extends TypeAdapter<MyClass> {
#Override public void write(JsonWriter out, MyClass myClass) throws IOException {
// implement the write method
}
#Override public MyClass read(JsonReader in) throws IOException {
// implement the read method
return ...;
}
}
I have to deserialize json like this:
{
"key1" : [
{
"hash1" : "value1_1",
"hash2" : "value1_2",
...
"hashN" : "value1_3",
"date" : "dateValue1"
},
{
"hash1" : "value2_1",
"hash2" : "value2_2",
...
"hashN" : "value2_3",
"date" : "dateValue2"
},
...
],
"key2": {
"description" : {
"hash1" : {
"description1" : "some text",
"description2" : "some text",
},
...
"hashN" : {
"description1" : "some text",
"description2" : "some text",
}
}
}
}
That json have set of unknow keys: hash1, hash2, ... hash2, and set of know keys: key1, key2, description, date, description1, description2.
I work with some custom rest client which use default GSON configuration to deserialize jsons to objects. And I can't change that configuration.
Using this rest client looks like this:
restClient.makeRequest(requestData, DataResponse.class, new RestResponseListener<DataResponse>()
{
#Override
public void onSuccessfulResponse(DataResponse responseData)
{
}
});
DataResponse class have to inherit from Response class from rest client package.
That rest client can't deserialize jsons like above so I decide to try deserializing to String or JsonObject and next in onSuccessfulResponse use custom deserializer.
I try to create below class to hold response:
public class DataResponse extends Response
{
private String key1;
private String key2;
public DataResponse()
{
}
}
Unfortunately I get exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 14 path
The question is, how to deserialize array from key1 and object from key2 to strings.
Or maybe is another solution.
Follow Lyubomyr Shaydariv advice I found two solutions.
Using JsonElement class
JsonElement is of course abstract so I have to use subclasses as below
public class DataResponse extends Response
{
private JsonArray key1;
private JsonObject key2;
public DataResponse()
{
}
//getters
}
In rest client callback I handled this data like this:
restClient.makeRequest(requestData, DataResponse.class, new RestResponseListener<DataResponse>()
{
#Override
public void onSuccessfulResponse(DataResponse responseData)
{
Type mapType = new TypeToken<Map<String, String>>(){}.getType();
Gson gson = new Gson();
for(JsonElement element : responseData.getKey1())
{
Map<String, String> map = gson.fromJson(element, mapType);
//do things
}
}
});
Match DataResponse class to json structure
I don't know why it doesn't work previously, but now does:
public class DataResponse extends Response
{
private List<Map<String, String>> key1;
private Key2SchemaClass key2;
public DataResponse()
{
}
public static class Key2SchemaClass
{
private List<Map<String, Description>> description;
}
public static class Description
{
private String description1;
private String description2;
}
//getters
}
I have a JSON that looks like this:
{
"results": {
"exchange": [
"site.com",
{
"currency": "usd",
"last_traded": "2015.24"
}
]
}
}
How do I get the last_traded value?
I wrote some POJO for this, but I can't seem to find a way to get the key-value inside exchange array.
public class ExchangeContainer {
#Expose
private Results results;
public Results getResults() {
return results;
}
public void setResults(Results results) {
this.results = results;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
public class Results {
#Expose
private List<String> exchange = new ArrayList<String>();
public List<String> getExchange() {
return exchange;
}
public void setExchange(List<String> exchange) {
this.exchange = exchange;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
I'm using GSON to deserialize this JSON.
So in order for me to traverse through the model is:
ExchangeContainer response;
String rate = response.getResults().getExchange().get(1); // how to continue?
and I'm stuck.
Implement interface using implements JsonDeserializer in your class
and override deserialize method.
Example link - http://www.javacreed.com/gson-deserialiser-example/
As you have created list of Strings by
private List<String> exchange = new ArrayList<String>();
& setting another list in created list i.e., in exchange
public void setExchange(List<String> exchange)
When you get value by invoking line
String rate = response.getResults().getExchange().get(1);
it contains "last_traded": "2015.24" is it right ?
Now to get 2015.24, you have following choices :-
String[] split(":")
String substring(int beginIndex)
I hope this will solve your problem.
I've tried to do this in another project, and as per the tutorials I've seen, I know I am on the right track, but I cannot get this parsing correctly:
(Much Simplified) JSON Output:
{
"data":{
"info":{
"username": "something"
"email" : "something"
}
..
..
}
I am trying to get "username" and "email using the following classes:
class ProfileResponse {
static Data data;
public static Data getData() {
return data;
}
public static void setData(Data data) {
ProfileResponse.data = data;
}
}
Class Data {
#SerializedName("info")
static Info info;
public static Info getInfo() {
return info;
}
public static void setInfo(Info info) {
Data.info = info;
}
}
class Info {
#SerializedName("username")
static String username;
#SerializedName("email")
static String email;
public static String getUsername() {
return username;
}
public static String getEmail() {
return email;
}
}
and Deserializing the JSON String (could it be a problem that it's a String?) like so:
Gson gson = new Gson();
gson.fromJson(response, ProfileResponse.class);
if (Info.getUsername() == null
|| Info.getUsername().isEmpty()) {
System.out.println("NO USERNAME");
} else {
System.out.println("USERNAME: "
+ Info.getUsername());
}
This is printing "NO USERNAME" each time it's run.
static fields are by default excluded from serialization/deserialization.
Remove all the static keywords from your classes (fields and methods), call fromJson() correctly, and you will then get the result you're looking for.
Gson instantiates an instance of your class(es) from your JSON. After modifying your classes, you then will do:
ProfileResponse pr = gson.fromJson(response, ProfileResponse.class);
I want to send json, which have another json object inside, like this
{
"key1": "value1",
"key2": "valu2",
"content": {
"nestedkey1": "nestedValue1",
"nestedkey2": "nestedValue2"
}
}
Object inside doesn't have any java representation, just string in json-format. How it can be correctly converted?
My approach is not correct, I always receive empty string for nested json. I used Map for this nested object, but empty map again.
public class Instance {
private String key1;
private int key2;
private String content;
public String getKey1 {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public BigDecimal getKey2() {
return key2;
}
public void setKey2(BigDecimal key2) {
this.key2 = key2;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
How is the JSON serialization being done? If you're not using Jackson, then you should be.
Jackson can take a Map and translate it to JSON just like you want, without any additional configuration. On the other hand, if you're using the Jersey JSON plugin, you'd have to write a subclass of Map, and add JAXB annotations to it - kind of a pain in the ass.