Gson - Named Object to Array - android

In the response from the server, the data is structured as named objects. I'm trying to figure out how I would convert it to an array with the details in specific fields.
Example response from the server.
{
"Value_1": { "Foo": "True", "Bar": "False"},
"Value_2": { "Foo": "False", "Bar": "False"},
"Value_3": { "Foo": "False", "Bar": "True"}
}
Example of preferred converted result from the server.
{[
{"Name": "Value_1",
"Details": [{"Name": "Foo", "Value": "True"},
{"Name": "Bar", "Value": "False"}]},
{"Name": "Value_2",
"Details": [{"Name": "Foo", "Value": "False"},
{"Name": "Bar", "Value": "False"}]},
{"Name": "Value_3",
"Details": [{"Name": "Foo", "Value": "False"},
{"Name": "Bar", "Value": "True"}]}
]}
How do tell gson to convert from the response to the preferred structure?

I was able to accomplish this in a fairly clean method using a custom JsonDeserializer. The main thing is JsonObject.entrySet(), which gives you the key/value pairs of the JsonObject so you can iterate over them.
First, when building your Retrofit client, add your custom JsonDeserializer.
gsonBuilder.registerTypeAdapter(MyModel::class.java, MyModelDeserializer())
And then implement it like this.
class MyModelDeserializer : JsonDeserializer<MyModel> {
override fun deserialize(json : JsonElement, typeOfT : Type?, context : JsonDeserializationContext?) : MyModel {
val jsonObject = json.asJsonObject
// Create a new ArrayList to store the values.
val list = ArrayList<MyModel>()
// Using entrySet, get each key/value pair. "Value_1: {...}"
for (entry in jsonObject.entrySet()) {
val valueName = entry.key // The key, which would be the name "Value_1"
// Creating a new ArrayList to contain all the data.
val vals = ArrayList<Pair<String, Boolean>>()
val values = entry.value.asJsonObject
// Using entrySet again for each value of the Object.
for (entry in values.entrySet()) {
vals.add(entry.key to entry.value.asBoolean)
}
// Create a new MyModel, with the correct name and values.
list.add(MyModel(valueName, vals))
}
return MyModel(list)
}
}

Related

Android - Convert json which has uncertain keys to map using kotlin

I have a json like this. I need to convert it to data class
{
"0": {
"id": "111",
"type": "1",
"items": [
{
"name": "Jack",
"value": "26",
"age": "0.0"
},
{
"name": "Lisa",
"value": "18",
"age": "1.0"
}
]
},
"1": {
"id": "222",
"type": "2",
"items": [
{
"name": "Brown",
"value": "23",
"age": "30.0"
},
{
"name": "Andy",
"value": "18",
"age": "23.0"
}
]
},
"className": "A01"
}
I define the following data class
data class Orders (
val className: String?,
val classes: Map<String, EachClass>
)
data class EachClass (
val id: String,
val type: String,
val items: List<Person>
)
data class Person (
val name: String,
val value: String,
val age: String
)
And the result always show
className=> A01, classes=> null
I searched the stackoverflow and they said using TypeToken. But I have a field called "className" which cannot be convert with EachClass object
val type = object : TypeToken<EachClass>() {}.type
val obj = Gson().fromJson(data, EachClass::class.java)
and I found TypeToken with HashMap<String, Object> is working but its ugly and I need to convert to data class myself.
I'm appreciate if someone can tell me the correct way to convert the json. Thanks!
Gson does not provide built-in functionality for this specific situation so you need to do some manual conversion, but luckily for your use case it is not that much work. The following approach should work:
Parse the JSON as Gson's JsonObject
Remove the className member and store it for later
Parse the JsonObject as Map<String, EachClass>
Construct an Orders instance from the results from step 2 and 3
The complete solution could look like this:
object OrdersDeserializer: JsonDeserializer<Orders> {
private val classesType = object: TypeToken<Map<String, EachClass>>() {}.type
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Orders {
val jsonObject = json.asJsonObject
val className = jsonObject.remove("className").asJsonPrimitive.asString
val classes: Map<String, EachClass> = context.deserialize(jsonObject, classesType)
return Orders(className, classes)
}
}
You would then register it like this:
val gson = GsonBuilder()
.registerTypeAdapter(Orders::class.java, OrdersDeserializer)
.create()
Alternatively you could also convert it to a regular class and use Gson's #JsonAdapter annotation on the Orders class to avoid having to register the deserializer manually.
Note: Normally is recommended to prefer TypeAdapter over JsonSerializer / JsonDeserializer to allow streaming the data for better performance. However, since you need to work on a JsonObject here anyway (therefore non-streaming) using TypeAdapter does not provide an advantage here and might only complicate the implementation a bit.

GSON TypeAdapter not being called during deserialization of an array

Because Gson uses reflection to build objects during deserialization via a no-arguments constructor, I am writing a TypeAdapter that will instead use the appropriate constructor for my object. I need this to happen because the object I am deserializing is a subclass of another and needs to call the super constructor appropriately.
I'm trying to deserialize a JSON array that looks something like this (this is example data, not the actual structure of my object):
[
{
"id": "...",
"name": "...",
"description": "...",
"others": [
{
"key1": "val",
"key2": "val"
},
...
]
},
{
"id": "...",
"name": "...",
"description": "...",
"others": [
{
"key1": "val",
"key2": "val"
},
...
]
},
...
]
But I want to use a TypeAdapter during the deserialization of the objects that comprise this array. To be clear, this is how I am initiating the Gson deserialization:
new GsonBuilder()
.registerTypeAdapter(new TypeToken<MyObject>() {}.getType(), new MyTypeAdapter())
.create()
.fromJson(inputJson, MyObject[].class);
And this is MyTypeAdapter:
class MyTypeAdapter : TypeAdapter<MyObject> {
private val type = object : TypeToken<MyObject>() {}.type
override fun write(out: JsonWriter, myObject: MyObject) {
Gson().toJson(myObject, type, out))
}
override fun read(in: JsonReader): MyObject {
Log.d("MyTypeAdapter", "Deserialization via custom TypeAdapter")
val objByReflection = new Gson().fromJson(in, type)
return MyObject(objByReflection.id, objByReflection.name, objByReflection.description, objByReflection.others)
}
}
However, I find that the TypeAdapter is not used for the deserialization; setting a breakpoint doesn't find it being hit, and I get no log output.
Is this happening because I am actually deserializing an array of MyObject? What would be a good way to remedy this?
Thanks!

Generate unique json object name gson in Kotlin

How would I be able to generate a new object based on the object names below. What I am trying to do here is that when there is no Object1, gson will generate Object1 with the values below and when there is Object1, gson will generate Object2 and so on.
{
"Object1": {
"Name": "Object1",
"Color": "Orange",
"Food": "Pizza"
},
"Object2": {
"Name": "Object2",
"Color": "Blue",
"Food": "Pineapple"
}
}
Generating an unique property in your class object means you could dynamically have a class lets say Example.
FoodData {
var object1 : Food?
var object2 : Food?
// And so on...
}
For me this seems like a structural problem. Your response should be specific to the data structure you have and need. A more acceptable approach would be:
[
{
"Name": "Object1",
"Color": "Orange",
"Food": "Pizza",
"someAttr" : "Object1"
},
{
"Name": "Object2",
"Color": "Blue",
"Food": "Pineapple",
"someAttr" : "Object2"
}
]
And then it would make more sense to have a list of food like :
FoodData {
var listFood: List<Food>?
}

How parse JSON with "key is value" style JSONObject to normal with GSON [duplicate]

This question already has answers here:
Array of JSON Object to Java POJO
(3 answers)
Closed 3 years ago.
I have this JSON file
"Items": [
{
"Name": "Id",
"Value": "102"
},
{
"Name": "TypeUid",
"Value": "333"
}, {
"Data": {
"Items": [
{
"Name": "Id",
"Value": "106"
},
{
"Name": "TypeUid",
"Value": "444"
},
{
"Name": "Uid",
"Value": "1322"
},
{
"Name": "Name",
"Value": "Alex"
}
]
In this file key-value pairs are kept inside JSONObject. How i can parse this file with GSON like this?:
"Items": {
"id" = 102,
"typeId" = 333,
"name" = "Alex"}
I don't understand how i can get normal POJO objects for GSON with this kind of JSON file.
You can simply create a model class to store the data like
class Items{
String/int id;
String/int typeId;
.
.
}
Then you can make a list which will hold the data like
List<Items> items = new ArrayList<>();
Then read the json string and parse it to form the list containing the data.
Refer this answer to convert json to list: Link

Unable parse the json data in android even by following the actual procedure

iam trying to parse the json data which is shown below.i got output for other Json response by following this procedure but for this i cant get data.
{
"totalResults": 7,
"startIndex": 1,
"hasMoreResults": false,
"itemsPerPage": 10,
"results": [
{
"offering_temp_id": {
"displayName": "Cool Course",
"id": "cours000000000004841",
"course_no": "00006081",
"version": null
},
"delivery_id": {
"displayName": "Instructor-Led",
"id": "eqcat000000000000004"
},
"student_id": {
"id": "emplo000000006156648",
"displayName": "Venkat Rao",
"person_no": "VRAO"
},
"reg_no": "00008341",
"wlist_on": "2017-08-17T08:59:39.843-0400",
"wlist_priority": 5,
"Max_Count": null,
"Current_Count": null,
"is_no_show": false,
"is_walk_in": false,
"offering_action_id": {
"id": "ofapr000000000013441",
"displayName": "00009081"
},
"class_id": {
"id": "class000000000006981",
"displayName": "Cool Course"
},
"elements_to_complete": 0,
"status": "100",
"id": "regdw000000000012581"
},
// total 7 fields
],
"facets": []
}
And iam using the parser procedure as follows
public class EnrollmentParser {
public ArrayList<EnrollmentData> getData(String respnose)//EnrollmentData is my pojo class contains 4 strings and its getters and setters
{
ArrayList<EnrollmentData> dataList = new ArrayList<>();
try {
JSONObject mainObj = new JSONObject(respnose);
JSONArray array = mainObj.getJSONArray("results");
for(int i = 0;i<array.length();i++)
{
EnrollmentData data = new EnrollmentData();
JSONObject resObj = array.getJSONObject(i);
data.setReg_num(resObj.getString("reg_no"));
data.setElements_to_complete(resObj.getString("elements_to_complete"));
data.setW_list_on(resObj.getString("wlist_on"));
data.setW_list_priority(resObj.getString("wlist_priority"));
dataList.add(data);
}
} catch (Exception e) {
e.printStackTrace();
}
return dataList;
}
}
when iam trying to display the dataList returned from above method i didnt get any data i got response like
Suggest me any changes that are required to get the response
There is no faulty parsing code, everything is fine (although better use optString).
By default , the toString function will return the type '#' reference value so
You need to override toString in EnrollmentData class to see the actual content inside EnrollmentData object
You can also collect your list objects as single string using
String str = Arrays.toString(yourList.toArray());
How to print out all the elements of a List in Java?
Why don't you use Gson to convert json into object easily instead of getting the field and set them one by one?
You may check out
https://github.com/google/gson

Categories

Resources