I have turned json data into classes in my app to use them in layout files. This is my Davaproperty class:
class DavaProperty : ArrayList<DavaProperty.DavaPropertyItem>(){
data class DavaPropertyItem(
val _links: Links,
val attributes: List<Any>,
val average_rating: String,
val backordered: Boolean,
val backorders: String,
val backorders_allowed: Boolean,
val button_text: String,
val catalog_visibility: String,
val categories: List<Category>,
val cross_sell_ids: List<Any>,
val date_created: String,
val date_created_gmt: String,
val date_modified: String,
val date_modified_gmt: String,
val date_on_sale_from: Any,
val date_on_sale_from_gmt: Any,
val date_on_sale_to: Any,
val date_on_sale_to_gmt: Any,
val default_attributes: List<Any>,
val description: String,
val dimensions: Dimensions,
val download_expiry: Int,
val download_limit: Int,
val downloadable: Boolean,
val downloads: List<Any>,
val external_url: String,
val featured: Boolean,
val grouped_products: List<Any>,
val has_options: Boolean,
val id: Int,
val images: List<Image>,
val low_stock_amount: Any,
val manage_stock: Boolean,
val menu_order: Int,
val meta_data: List<MetaData>,
val name: String,
val on_sale: Boolean,
val parent_id: Int,
val permalink: String,
val price: String,
val price_html: String,
val purchasable: Boolean,
val purchase_note: String,
val rating_count: Int,
val regular_price: String,
val related_ids: List<Int>,
val reviews_allowed: Boolean,
val sale_price: String,
val shipping_class: String,
val shipping_class_id: Int,
val shipping_required: Boolean,
val shipping_taxable: Boolean,
val short_description: String,
val sku: String,
val slug: String,
val sold_individually: Boolean,
val status: String,
val stock_quantity: Any,
val stock_status: String,
val tags: List<Any>,
val tax_class: String,
val tax_status: String,
val total_sales: Int,
val type: String,
val upsell_ids: List<Any>,
val variations: List<Any>,
val virtual: Boolean,
val weight: String
) {
data class Links(
val collection: List<Collection>,
val self: List<Self>
) {
data class Collection(
val href: String
)
data class Self(
val href: String
)
}
data class Category(
val id: Int,
val name: String,
val slug: String
)
data class Dimensions(
val height: String,
val length: String,
val width: String
)
data class Image(
val alt: String,
val date_created: String,
val date_created_gmt: String,
val date_modified: String,
val date_modified_gmt: String,
val id: Int,
val name: String,
val src: String
)
data class MetaData(
val id: Int,
val key: String,
val value: Any
)
}
}
And this is my item_view.xml that I have to associate items to classes:
when I run the program, it gives me this error:
Could not find accessor com.example.android.davadoctor.network.DavaProperty.DavaPropertyItem
Where am I making the mistake?
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
variable name="property" type="com.example.android.davadoctor.network.DavaProperty"
<ImageView
android:id="#+id/dava_image"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop"
app:imageUrl="#{property.DavaPropertyItem.Image.src}"
android:contentDescription="#null"
android:padding="3dp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
>
<TextView
android:id="#+id/price_amount"
android:layout_width="60dp"
android:layout_height="20dp"
android:text="#{Double.toString(property.DavaPropertyItem.price)}"/>
<TextView
android:id="#+id/price_lable"
android:layout_width="60dp"
android:layout_height="20dp"
android:text="#string/price_lable"/>
<TextView
android:id="#+id/title"
android:layout_width="100dp"
android:layout_height="20dp"
android:text="#{property.DavaPropertyItem.name}"/>
</LinearLayout>
</LinearLayout>
</layout>
Related
I have JSON object like this
{
"codemap":{
"codeOfItem1":"titleOfItem1",
"codeOfItem2":"titleOfItem2",
"codeOfItem3":"titleOfItem3",
"codeOfItem4":"titleOfItem4"
},
"items":{
"titleOfItem1":{
"attribute1":"value1",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item1",
"subattr2":"value1_of_subattr2_for_item1"
}
},
"titleOfItem2":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item2",
"subattr2":"value1_of_subattr2_for_item2"
}
},
"titleOfItem3":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item3",
"subattr2":"value1_of_subattr2_for_item3"
}
},
"titleOfItem4":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item4",
"subattr2":"value1_of_subattr2_for_item4"
}
}
}
}
How to parse it using GSON in Kotlin ?
(Problem is that strings like titleOfItemXXX is both values in codemap map and key names in items map
I don't really like idea to go fully manual way like in How to parse this Json with no object name
Update:
I don't want to get scheme like this (this is from Kotlin-to-JSON Android Studio Plugin)
import com.google.gson.annotations.SerializedName
data class x1(
#SerializedName("codemap")
val codemap: Codemap,
#SerializedName("items")
val items: Items
) {
data class Codemap(
#SerializedName("codeOfItem1")
val codeOfItem1: String, // titleOfItem1
#SerializedName("codeOfItem2")
val codeOfItem2: String, // titleOfItem2
#SerializedName("codeOfItem3")
val codeOfItem3: String, // titleOfItem3
#SerializedName("codeOfItem4")
val codeOfItem4: String // titleOfItem4
)
data class Items(
#SerializedName("titleOfItem1")
val titleOfItem1: TitleOfItem1,
#SerializedName("titleOfItem2")
val titleOfItem2: TitleOfItem2,
#SerializedName("titleOfItem3")
val titleOfItem3: TitleOfItem3,
#SerializedName("titleOfItem4")
val titleOfItem4: TitleOfItem4
) {
data class TitleOfItem1(
#SerializedName("attribute1")
val attribute1: String, // value1
#SerializedName("atttribute2")
val atttribute2: Atttribute2
) {
data class Atttribute2(
#SerializedName("subattr1")
val subattr1: String, // value1_of_subattr1_for_item1
#SerializedName("subattr2")
val subattr2: String // value1_of_subattr2_for_item1
)
}
data class TitleOfItem2(
#SerializedName("attribute1")
val attribute1: String, // value2
#SerializedName("atttribute2")
val atttribute2: Atttribute2
) {
data class Atttribute2(
#SerializedName("subattr1")
val subattr1: String, // value1_of_subattr1_for_item2
#SerializedName("subattr2")
val subattr2: String // value1_of_subattr2_for_item2
)
}
data class TitleOfItem3(
#SerializedName("attribute1")
val attribute1: String, // value2
#SerializedName("atttribute2")
val atttribute2: Atttribute2
) {
data class Atttribute2(
#SerializedName("subattr1")
val subattr1: String, // value1_of_subattr1_for_item3
#SerializedName("subattr2")
val subattr2: String // value1_of_subattr2_for_item3
)
}
data class TitleOfItem4(
#SerializedName("attribute1")
val attribute1: String, // value2
#SerializedName("atttribute2")
val atttribute2: Atttribute2
) {
data class Atttribute2(
#SerializedName("subattr1")
val subattr1: String, // value1_of_subattr1_for_item4
#SerializedName("subattr2")
val subattr2: String // value1_of_subattr2_for_item4
)
}
}
}
because I don't really known how much items I will have and which names they will use in production.
I think you want dynamic-named property in the schema. In this case, declare it as Map:
data class Schema(val codemap: Map<String, String>, val items: Map<String, Item>) {
data class Item(val attribute1: String, val atttribute2: Attr2) {
data class Attr2(val subattr1: String, val subattr2: String)
}
}
fun gsonDemo() {
val json = """
{
"codemap":{
"codeOfItem1":"titleOfItem1",
"codeOfItem2":"titleOfItem2",
"codeOfItem3":"titleOfItem3",
"codeOfItem4":"titleOfItem4"
},
"items":{
"titleOfItem1":{
"attribute1":"value1",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item1",
"subattr2":"value1_of_subattr2_for_item1"
}
},
"titleOfItem2":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item2",
"subattr2":"value1_of_subattr2_for_item2"
}
},
"titleOfItem3":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item3",
"subattr2":"value1_of_subattr2_for_item3"
}
},
"titleOfItem4":{
"attribute1":"value2",
"atttribute2":{
"subattr1":"value1_of_subattr1_for_item4",
"subattr2":"value1_of_subattr2_for_item4"
}
}
}
}
""".trimIndent()
val obj = Gson().fromJson(json, Schema::class.java)
println(obj.items[obj.codemap["codeOfItem3"]]?.atttribute2?.subattr1) // print value1_of_subattr1_for_item3
}
Note that Gson won't fail if some properties are missing, so all the values of properties may be null, although they are declared as non-nullable
Looks like I forget about simple ways :(
Working answer:
data class TopLevel (
#SerializedName("codemap")
val codemap: Map<String, String>,
#SerializedName("items")
val items: Map<String, Item>
)
data class Item (
#SerializedName("attribute1")
val attribute1: Attribute1,
#SerializedName("attribute2")
val attribute2: Attribute2
)
data class Attribute2 (
#SerializedName("subattr1")
val subattr1: String,
#SerializedName("subattr2")
val subattr1: String
)
enum class Attribute1 {
#SerializedName("DarkSide")
DarkSide,
#SerializedName("LightSide")
LightSide
}
var gson: Gson = Gson()
val str=... //string with source JSON
val result = gson.fromJson(str, TopLeve::class.java)
Everything appears to work correctly.
I have a json like this:
{"ok":true,"result":[{"update_id":853803195,
"message":{"message_id":313,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594723984,"text":"/start","entities":[{"offset":0,"length":6,"type":"bot_command"}]}},{"update_id":853803196,
"message":{"message_id":314,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594723986,"text":"e"}},{"update_id":853803197,
"message":{"message_id":325,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594734252,"text":"\ud83d\ude06"}},{"update_id":853803198,
"message":{"message_id":328,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594736358,"text":"5"}}]}
I'm using klaxon library.
Like in the first example I created a class:
class Response(val ok: Boolean, val result: String)
I was trying to save the second parameter to a string, so I could parse it, too, later. But I get a exception like this:
Unable to instantiate Response with parameters [ok: true, result: [, , , ]]
I tried making result a JsonObject (or a JsonArray of JsonObjects) and got this
Unable to instantiate JsonObject with parameters []
The only thing that more or less worked was a List<Any>. Result becomes a list of java objects like this:
[java.lang.Object#680362a, java.lang.Object#3569edd5, java.lang.Object#1f651cd8, java.lang.Object#7d0332e1]
But I don't know how to deal with them. So my question is what do I do? How do I get the result I will be able to work with?
Try this class
import com.beust.klaxon.*
private val klaxon = Klaxon()
data class User (
val ok: Boolean,
val result: List<Result>
) {
public fun toJson() = klaxon.toJsonString(this)
companion object {
public fun fromJson(json: String) = klaxon.parse<User>(json)
}
}
data class Result (
#Json(name = "update_id")
val updateID: Long,
val message: Message
)
data class Message (
#Json(name = "message_id")
val messageID: Long,
val from: From,
val chat: Chat,
val date: Long,
val text: String,
val entities: List<Entity>? = null
)
data class Chat (
val id: Long,
#Json(name = "first_name")
val firstName: String,
val username: String,
val type: String
)
data class Entity (
val offset: Long,
val length: Long,
val type: String
)
data class From (
val id: Long,
#Json(name = "is_bot")
val isBot: Boolean,
#Json(name = "first_name")
val firstName: String,
val username: String,
#Json(name = "language_code")
val languageCode: String
)
I need to do a task: paging list of news.
To do it I took a sample from googlesample/architecthurecomponents/PagingWithNetworkSample and encounter with this question. Question is about code from Google sample to parse JSON file.
JSON url: https://www.reddit.com/r/androiddev/hot.json
POJO file:
#Entity(tableName = "posts",
indices = [Index(value = ["subreddit"], unique = false)])
data class RedditPost(
#PrimaryKey
#SerializedName("name")
val name: String,
#SerializedName("title")
val title: String,
#SerializedName("score")
val score: Int,
#SerializedName("author")
val author: String,
#SerializedName("subreddit") // this seems mutable but fine for a demo
#ColumnInfo(collate = ColumnInfo.NOCASE)
val subreddit: String,
#SerializedName("num_comments")
val num_comments: Int,
#SerializedName("created_utc")
val created: Long,
val thumbnail: String?,
val url: String?) {
// to be consistent w/ changing backend order, we need to keep a data like this
var indexInResponse: Int = -1
}
and this is an API interface:
interface RedditApi {
#GET("/r/{subreddit}/hot.json")
fun getTop(
#Path("subreddit") subreddit: String,
#Query("limit") limit: Int): Call<ListingResponse>
#GET("/r/{subreddit}/hot.json")
fun getTopAfter(
#Path("subreddit") subreddit: String,
#Query("after") after: String,
#Query("limit") limit: Int): Call<ListingResponse>
#GET("/r/{subreddit}/hot.json")
fun getTopBefore(
#Path("subreddit") subreddit: String,
#Query("before") before: String,
#Query("limit") limit: Int): Call<ListingResponse>
class ListingResponse(val data: ListingData)
class ListingData(
val children: List<RedditChildrenResponse>,
val after: String?,
val before: String?
)
data class RedditChildrenResponse(val data: RedditPost)
companion object {
private const val BASE_URL = "https://www.reddit.com/"
fun create(): RedditApi = create(HttpUrl.parse(BASE_URL)!!)
fun create(httpUrl: HttpUrl): RedditApi {
val logger = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
Log.d("API", it)
})
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.baseUrl(httpUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RedditApi::class.java)
}
}
}
The question is: how does the API request exactly finds what we need, a children: [...], which represent a list of posts? Because a children: [...] resides inside object and in code we don't have a POJO with #Serialized("children")field. Only a pojo for items inside children: [...]. I tried to implement this approach specific to my json, but it returns a null value.
Thanks everyone for help.
You don't have to add #SerializedName annotation if the name of the field in POJO is the same as the name of the field in JSON. That's why class ListingResponse(val data: ListingData) can be mapped to
{
"kind": "Listing",
"data": ...
}
I've created the classes of Eqs and Service, got the service objects but can't get the list of eqs. Can anyone help me with this?
This the Eqs class
data class Eqs(
val name: String,
val imageUrl: String,
val description: String?,
val responsible: String
)
That's the Service class which gets its values
data class Service(
val title: String,
val servings: Int,
val eqs: List<Eqs>
) {
companion object {
fun getServicesFromFile(filename: String, context: Context): ArrayList<Service> {
val serviceList = ArrayList<Service>()
try {
// Load data
val jsonString = loadJsonFromAsset("services.json", context)
val json = JSONObject(jsonString)
val services = json.getJSONArray("services")
(0 until services.length()).mapTo(serviceList) {
Service(services.getJSONObject(it).getString("title"),
services.getJSONObject(it).getInt("servings"),
}
} catch (e: JSONException) {
e.printStackTrace()
}
return serviceList
}
I can't get the List of Eqs in my getServicesFromFile function. How to parse and get it correctly?
I recommend you to use Jackson library. It's simple and saves you a lot of time. You can find it's documentation here: https://www.baeldung.com/jackson-kotlin
You also can use some websites to generate the data class needed for Jackson like https://app.quicktype.io/
Use Json to Kotlin plugin
In tool bar of android studio Code >> Generate and copy & paste you API into it and give the class name
[
{
"id": 1,
"name" : "Madoldoowa",
"description": "Madol Doova (මඩොල් දූව) is a children's novel and coming-of-age story written by Sri Lankan writer
Martin Wickramasinghe and first published in 1947",
"language" : "Sinhala",
"isbn" : "ISBN232673434",
"file_size" : 300,
"no_of_pages" : 500,
"price" : 970,
"ratings" : "5.1K",
"cover_page" : "https://upload.wikimedia.org/wikipedia/en/5/5c/MadolDoova.jpg",
"author" : {
"name" : "Martin Wickramasinghe"
}
]
data class Model(
val author: Author,
val cover_page: String,
val description: String,
val file_size: Int,
val id: Int,
val isbn: String,
val language: String,
val name: String,
val no_of_pages: Int,
val price: Int,
val ratings: String
)
data class Author(
val name: String
)
I have JSON in following format:
{
"id": 1913548255,
"notification": "NotificationReceived",
"deviceGuid": "e60d6085-2aba-48e9-b1c3-73c673e414be",
"timestamp": "2016-01-28T20:34:34.167",
"parameters": {
"jsonString": "{\"mac\":\"bc6a29abd973\",\"uuid\":\"f000aa1104514000b000000000000000\",\"value\":0.27328648477047685}"
}
}
I want to deserialize it to get following classes, so that :
case class Parameters(mac: String, uuid: String, value: Double)
case class Notification(id: BigInt, notification: String, deviceGuid: String, timestamp: String, perameters: Parameters)
I know i need to write CustomSerializer. But i don't have much experience. Please, guide me. Thanks for help.
I decided no to deal with deserializer, but do it in ordinary way. I am posting the code so that it may help someone.
case class Parameters(mac: String, uuid: String, value: Double)
case class Notification(id: Int, notification: String, deviceGuid: String, timestamp: String, parameters: Map[String, String])
case class FinalNotification(id: Int, notification: String, device_guid: String, timestamp: String, mac: String, uuid: String, value: Double)
implicit val formats = DefaultFormats
val n = parse(v).extract[Notification]
def convertJson(json: Option[String]): Parameters = json match {
case None => throw new IllegalArgumentException("Json can't be converted. ")
case Some(j) => parse(j).extract[Parameters]
}
val param = convertJson(n.parameters.get("jsonString"))
FinalNotification(n.id, n.notification, n.deviceGuid, n.timestamp, param.mac, param.uuid, param.value)