I want to parse JSON like below:
{
"result": 0,
"list": [
{
"id": 58,
"type": "58",
"name": "fooGroup",
"foos": {
"id": "1",
"name": "33",
}
]
}
If I define models like this:
data class Response(val result: Int,
#SerializedName("list") val fooGroup: List<FooGroupResponse>)
data class FooGroupResponse(val id: Int, val type: String, val name: String,
#SerializedName("foos") val fooGroup: List<Foo>?)
data class Foo(val id: Int, val name: String)
then everything works fine.
Right now I want to take these out as a model:
"id": 58,
"type": "58",
"name": "fooGroup",
That is add one more model FooGroup like below:
data class Response(val result: Int,
#SerializedName("list") val fooGroup: List<FooGroupResponse>)
data class FooGroupResponse(val fooGroup: FoolGroup,
#SerializedName("foos") val fooGroup: List<Foo>?)
data class Foo(val id: Int, val name: String)
data class FooGroup(val id: Int, val type: String, val name: String)
But there's no #SerializedName can be set for FooGroup, is it possible?
Thanks.
I think you will have to restructure your JSON. The parser is going to make a faithful representation of the JSON string as a Java object. Your first example is the faithful representation. What you want to do is not faithful to the JSON received.
{
"result": 0,
"list": [
{
"fooGroup": {
"id": 58,
"type": "58",
"name": "fooGroup"
},
"foos": [
{
"id": "1",
"name": "33"
}
]
}
]
}
BUT you can do it manually where you parse the object yourself. Here is a how to with GSON but it should be easily convertible if you prefer another lib.
https://www.woolha.com/tutorials/retrofit-2-define-custom-gson-converter-factory
Simply pull "id", "type", "name" from the JSON as its deserializing and make a composite data class.
data class FooGroup(val id: Int, val type: String, val name: String)
//You deserialize you JSON into a List of Foos
data class Foos(val foo: Foo, val fooGroup : FooGroup)
Related
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.
I have this json response from the api, and the response can't be change
data class Weather (
val category: String,
val id: String,
val meta: Meta
)
data class Meta (
val id: String,
val name: String,
val details: String
)
Json respose
{
"weather" : {
"category": "articles",
"id": "1",
"meta": {
"id": "1",
"name": "The shortest article. Ever.",
"details": "see"
},
"weather" : {
"category": "articles",
"id": "2",
"meta": []
}
If meta is empty, it come with an array but if not empty, it come with object.
Retrofit throws
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
the api can't be modify so this has to be fix on client end. How can I solve this
You can not make this possible
meta object must be an object when have value and null when do not have any value, or an array with value when exist and empty when not exist.
meta can not be an array and object in the same time.
this is very bad mistake from who created this response body.
You can use Any type for meta. and put check at your code level like this.
data class Weather (
val category: String,
val id: String,
val meta: Any
)
if(meta is Meta)
parse it to your Meta object
else
parse it to list
i tried this :
interface MYAPI {
#GET("get-languages")
fun getdata() : Call<List<Data.Language>>
}
this is my api service
{
"message": "success",
"data": {
"language": [ {"id": 5,
"name": "English",
"icon": "19638193-en.png"
},
{
"id": 6,"name": "turkish","icon": "19638199-tr.png"}
]
}
}
{
"message":"success",
"data":{
"language":[]
}
}
Share your response model but you will need the "data" attribute that is a language model. If you use directly the language model then won´t works.
So a possible data could be:
data class LanguageResponse(val id: Int, val name: String, val icon: String)
data class LanguagesResponse(val language: List<LanguageResponse>)
data class DataLanguageResponse(val data: LanguagesResponse)
And your call:
interface MYAPI {
#GET("get-languages")
fun getdata() : Call<DataLanguageResponse>
}
Solved..
i add to AndroidManifest :
android:usesCleartextTraffic="true"
Can anybody say where I am doing wrong. I have json like that
[
{
"id": "1",
"name": "ff",
"surname": "ggg",
"cap": "10000"
},
{
"id": "1",
"name": "aaa",
"surname": "hhh",
"cap": "22222"
},
{
"id": "1",
"name": "rrr",
"surname": "hhhhhdr",
"cap": "33333"
},
{
"id": "1",
"name": "hhh",
"surname": "qqqqq",
"cap": "44444"
}
]
And I parse to this class.
data class ResponseList(
val capList: List<Response>?
) {
data class Response(
#JsonProperty("id")
val id: String,
#JsonProperty("name")
val name: String,
#JsonProperty("surname")
val surname: String,
#JsonProperty("cap")
val cap: String
)
}
When I try to parse it the list is always null and if I try to test it I have this error:
Cannot deserialize value of type com.myapp.ResponseList from Array value (token JsonToken.START_ARRAY)
just class Response is needed, like following:
fun test(){
val jsonStr = "your json str"
val mapper = ObjectMapper()
val lendReco: List<Response> =
mapper.readValue(jsonStr, object : TypeReference<List<Response?>?>() {})
}
data class Response(
#JsonProperty("id")
val id: String,
#JsonProperty("name")
val name: String,
#JsonProperty("surname")
val surname: String,
#JsonProperty("cap")
val cap: String
)
from my local Django Rest Framework service I get the following JSON output:
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"created": "2020-04-18T16:00:16.060915Z",
"name": "Germany",
"groups": [
{
"id": 1,
"created": "2020-04-18T16:03:11.138661Z",
"name": "MyGroup1",
"owner_id": 1
},
{
"id": 2,
"created": "2020-04-18T16:03:20.701660Z",
"name": "MyGroup2",
"owner_id": 1
},
...
Each Country can have many Groups. For this I have created the following data classes in my Android App project:
#JsonClass(generateAdapter = true)
data class NetworkCountryContainer(
val count: Long,
val next: String?,
val previous: String?,
val results: List<Country>
)
#Entity(tableName = "country_table")
#JsonClass(generateAdapter = true)
data class Country(
#PrimaryKey
#Json(name="id")
val countryId : Int,
#Json(name="name")
val countryName: String,
#Json(name="groups")
val groupList: List<Group> // <--- this field causes the ERROR
)
#Entity(tableName = "group_table")
#JsonClass(generateAdapter = true)
data class Group(
#PrimaryKey
#Json(name="id")
val groupId : Int,
#Json(name="name")
val groupName: String,
#Json(name="owner_id")
val ownerId: Int
)
Android Studio tells me this:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Why I need a TypeConverter ? And how can I build one ?