I am parsing a json and than i need to populate the values on the graph, the response which i am getting as below
{
"message": "Successful.",
"data": {
"stats": {
"Total Transactions": 1,
"Today Transactions": 0,
"Today Pending Transactions": 0,
"Payment links created": 2,
"Api payments": 0
},
"otherData": {
"countiesData": {
"2021": {
"US": 1
}
},
"transactionTypesData": {
"2021": {
"PAYMENT_LINK": 1
}
},
"graphData": {
"2021": {
"SUCCESS": {
"1": 0,
"2": 0,
"3": 0,
"4": 0,
"5": 0,
"6": 0,
"7": 0,
"8": 0,
"9": 1,
"10": 0,
"11": 0,
"12": 0
}
}
},
"currentMoth": 3200,
"preMonthAvr": 3200
}
},
"code": 200,
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9."
}
I have created the model data for graphData within otherData.
data class OtherData(
var countiesData : Any,
var transactionTypesData: Any,
var graphData : HashMap<String,HashMap<String,HashMap<String,String>>>
)
i got the year with in graphData by these lines but i am unable to get other values within year object.
it.data.data.otherData.graphData.let { graphYear->
if (graphYear.isNotEmpty()){
tvYearForRevenueReport.text = graphYear.keys.elementAt(0)
tvFirstGraphLabel.text = graphYear.getValue(graphYear.keys.elementAt(0)) // giving error at this line
}
}
so my question is what i am doing it is the best way to do it? if it is than how, and if there is any work around will be glad to hear.
Using Gson I was able to parse your JSON:
Creating a Data class:
data class Data(
val data: InnerData
)
And an InnerData class:
data class InnerData(
val otherData: OtherData
)
And using your OtherData class:
data class OtherData(
var countiesData : Any,
var transactionTypesData: Any,
var graphData : HashMap<String,HashMap<String,HashMap<String,String>>>
)
Calling Gson to parse the JSON input:
val parsed = Gson().fromJson(json, Data::class.java)
I've used your JSON as an input and everything was working as expected.
Related
I've been running into the following exception at runtime with the debugger trying to de-serialize data from my Algolia index for my Kotlin Android recipe app I am trying to create by using the Kotlinx.Serialization library. The app compiles and runs fine, but no results show on the UI.
kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset -1: Failed to parse 'int'.
JSON input: {"amount":1.5,"name":"green beans","original":"1.5 pounds of green beans","unit":"pounds","unitLong":"pounds","unitShort":"lbs"}
Now from the looks this exception, It looks like the de-serializer is getting confused try to de-serialize my Ingredients data class. How would I go about de-serializing it?.
Example JSON data that is being sent over.
{
"cuisine": "European",
"diet": "Vegetarian",
"difficulty": 2,
"dishType": "Dinner",
"duration": 30,
"durationUnit": "minutes",
"image": "https://c.recipeland.com/images/r/1396/12f9fc271d8f1bfac5f6_550.jpg",
"ingredients": [
{
"amount": 1.5,
"name": "green beans",
"original": "1.5 pounds of green beans",
"unit": "pounds",
"unitLong": "pounds",
"unitShort": "lbs"
},
{
"amount": 1,
"name": "onion",
"original": "1.5 medium onion",
"unit": "medium",
"unitLong": "medium",
"unitShort": "med"
},
{
"amount": 2,
"name": "garlic",
"original": "2 teaspoons of garlic",
"unit": "teaspoons",
"unitLong": "teaspoons",
"unitShort": "tsps"
},
{
"amount": 1,
"name": "olive oil",
"original": "1 teaspoon olive oil",
"unit": "teaspoon",
"unitLong": "teaspoon",
"unitShort": "tsps"
},
{
"amount": 1,
"name": "mushrooms",
"original": "1 cup mushrooms",
"unit": "cup",
"unitLong": "cup",
"unitShort": "cup"
},
{
"amount": 1,
"name": "cherry tomatoes",
"original": "1 cup cherry tomatoes",
"unit": "cup",
"unitLong": "cup",
"unitShort": "cup"
}
],
"name": "Green Beans with Mushrooms and Cherry Tomatoes",
"preparation": [
"Steam green beans until tender.",
"Drain and set aside. Sauté onion and garlic in a medium skillet coated with olive oil, until tender. About 2 to 3 minutes.",
"Add mushrooms and sauté until tender. Stir in green beans and tomotoes until heated."
],
"yield": 4,
"objectID": "0"
}
I have my data classes for a recipe set up as the following:
Recipe.kt
#IgnoreExtraProperties
#Serializable
data class Recipe(
var difficulty: Int = 0,
var dishType: String? = null,
var duration: Int = 0,
var durationUnit: String? = null,
var image: String? = null,
var diet: String? = null,
var cuisine: String? = null,
var name: String? = null,
var ingredients: List<Ingredient> = emptyList(),
var preparation: List<String> = emptyList(),
var yield: Int = 0
) {
Ingredient.kt
#Serializable
data class Ingredient(
var amount: Int = 0,
var name: String? = null,
var original: String? = null, // Original text of the ingredient
var unit: String? = null,
var unitLong: String? = null,
var unitShort: String? = null
)
This block of code I got from Algolia's getting started guide for InstantSearch Android that de-serializes the data from the index.
private val datasourceFactory = SearcherSingleIndexDataSource.Factory(searcher) { hit ->
hit.deserialize(Recipe.serializer()) // Problem line I assume
}
val pagedListConfig = PagedList.Config.Builder().setPageSize(50).build()
val recipes: LiveData<PagedList<Recipe>> =
LivePagedListBuilder(datasourceFactory, pagedListConfig).build()
val searchBox =
SearchBoxConnectorPagedList(searcher, listOf(recipes))
I've tried to manually create the object by using the following code, but I run into issues when trying to create the list of ingredients.
val dataSourceFactory = SearcherSingleIndexDataSource.Factory(searcher) { hit ->
Recipe(
hit.json.getPrimitive("difficulty").content.toInt(),
hit.json.getPrimitive("dishType").content,
hit.json.getPrimitive("duration").content.toInt(),
hit.json.getPrimitive("durationUnit").content,
hit.json.getPrimitive("image").content,
hit.json.getPrimitive("diet").content,
hit.json.getPrimitive("cuisine").content,
hit.json.getPrimitive("name").content,
listOf(
Ingredient(
hit.json.getPrimitive("amount").content.toInt(),
hit.json.getPrimitive("name").content,
hit.json.getPrimitive("original").content,
hit.json.getPrimitive("unit").content,
hit.json.getPrimitive("unitLong").content,
hit.json.getPrimitive("unitShort").content
)
),
hit.json.getArray("preparation").content.map { prep -> prep.content },
hit.json.getPrimitive("yield").content.toInt()
)
}
I'm not 100% sure if I'm properly creating the preparation property member correctly as well as the whole creating the list of ingredients has side-tracked me. Any help would be greatly be appreciated and I apologize for my first post on here being a long one. I've been going at this for a couple of days already and I'm stumped as to what to do next.
As you can see this line:
kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset -1: Failed to parse 'int'.
Here JsonDecodingException exception occur that's why it is not giving proper response. You must check your all data classes are same variable which is in JSON Object.
Here I found 1 issue in your data class, First check this JSON Reposne:
"amount": 1.5
and now check your data class, which has var amount: Int = 0
#Serializable
data class Ingredient(
var amount: Int = 0,
var name: String? = null,
var original: String? = null, // Original text of the ingredient
var unit: String? = null,
var unitLong: String? = null,
var unitShort: String? = null
)
Here JSON Object is in Float and you are storing in it Int, which may cause exception. Make sure all values in data class are proper.
Or for work around you just make String all variable in data class to check all response show proper, than after just convert them to Int, Float according to your requirements.
how can I store a specific value of a key from json response into a variable
{
"results": [
{
"name": ryan,
"roll_id": 64,
"class_id": 310,
"net_id": 95,
},
],
};
above is the json response :-
val gson = GsonBuilder().create()
val ListV = gson.fromJson(body, HomeClass::class.java)
after these 2 lines I'm totally clueless how to do it I've gone through Internet but It was hard for me to understand how to proceed further.
Your Json Structure will be
{
"results": [
{
"name": "Amiyo",
"roll_id": 1,
"class_id": 10,
"net_id": 91
},
{
....
}
]
}
Data class should be
data class HomeClass (
#SerializedName("results") val results : List<Results>
)
data class Results (
#SerializedName("name") val name : String,
#SerializedName("roll_id") val roll_id : Int,
#SerializedName("class_id") val class_id : Int,
#SerializedName("net_id") val net_id : Int
)
fromJson
val listData = gson.fromJson(jsonData, HomeClass::class.java)
Then
val totalSize = 0 until listData!!.size
if(totalSize.size>0)
{
for (i in totalSize)
{
//Your Code i==Position
}
}
I'm quite new to development.
I know how to fetch from simple api's and store the data in kotlin data class something like this:
data class Property(
val id: String,
#Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double
)
But how can i fetch and store data sturctue like this?
{
"Thailand": [
{
"date": "2020-1-22",
"confirmed": 2,
"deaths": 0,
"recovered": 0
},
{
"date": "2020-1-23",
"confirmed": 3,
"deaths": 0,
"recovered": 0
},
...
],
...
}
As #CommonsWare suggested in the comments, I was able to get the data like this:
fun getPropeties():
Call<Map<String, List<Property>>>
}
I have a JSON response, which looks like this:
{
"equipment_layer": [
{
"id": 2,
"name": "Gateway",
"detail": "All gateways"
},
{
"id": 3,
"name": "Node",
"detail": "All Nodes"
},
{
"id": 1,
"name": "Miscellaneous",
"detail": "All miscellaneous assets"
},
{
"id": 4,
"name": "Sensors",
"detail": "All Sensors"
},
{
"id": 5,
"name": "IRM",
"detail": "Installation required material"
},
{
"id": 6,
"name": "Communication",
"detail": "All communication devices such as Cellular Router, ETU etc. which are purely communication"
}
],
"data": {
"1": [
{
"equipment_id": 353,
"item_quantity": 1,
"name": "DC Current Transformer (20mm) (Old)",
"shortcode": "SNS-DCI-CT20m-R1A",
"part_number": "718,804,805,",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
},
{
"equipment_id": 357,
"item_quantity": 1,
"name": "Fuel Sensor - B4 (Old)",
"shortcode": "SNS-FUL-PSR-R1A",
"part_number": "718,810,811",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
}
],
"2": [
{
"equipment_id": 345,
"item_quantity": 1,
"name": "RTU (Old)",
"shortcode": "RAN-RTU-PMCL-R1A",
"part_number": "787,788,789",
"equipment_layer_id": 2,
"equipment_layer_name": "Gateway"
}
],
"3": [
{
"equipment_id": 356,
"item_quantity": 1,
"name": "Battery Analyzer (Product) (Old)",
"shortcode": "RAN-BAM-PMCL-R1A",
"part_number": "787,808,809",
"equipment_layer_id": 3,
"equipment_layer_name": "Node"
}
],
"4": [
{
"equipment_id": 346,
"item_quantity": 1,
"name": "DC Current Transformer (30mm) (Old)",
"shortcode": "SNS-CT-DCI-R1A",
"part_number": "718,792,793",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
},
{
"equipment_id": 350,
"item_quantity": 1,
"name": "AC Block CT (Old)",
"shortcode": "SNS-ACI-BLK-R1A",
"part_number": "718,790,791",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
}
]
}
}
Now the part after the "data" label is dynamic, in a response I can have subarrays of "1", "2" but not of "3" or "4".The POJO of the data inside is same as you can see. So how can I parse this data? I'm using Rerofit2 with Gson.converterfactory. I've tried jsonchema2pojo as well but the data inside the "data" object is not showing up.
I've tried to follow this method:
Parsing Retrofit2 result using Gson with different JSON structures but I can't seem to trigger the UnrwapConverter.
This is my converterfactory implementation:
internal class UnwrappingGsonConverterFactory private constructor(private val gson: Gson) : Converter.Factory() {
override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *> ?{
if (!needsUnwrapping(annotations)) {
return super.responseBodyConverter(type, annotations, retrofit)
}
val typeAdapter = gson.getAdapter(TypeToken.get(type))
return UnwrappingResponseConverter(typeAdapter)
}
private class UnwrappingResponseConverter (private val typeAdapter: TypeAdapter<*>) : Converter<ResponseBody, Any> , AnkoLogger{
#Throws(IOException::class)
override fun convert(responseBody: ResponseBody): Any? {
responseBody.use { responseBody ->
JsonReader(responseBody.charStream()).use({ jsonReader ->
// Checking if the JSON document current value is null
val token = jsonReader.peek()
if (token === JsonToken.NULL) {
return null
}
// If it's an object, expect `{`
jsonReader.beginObject()
var value: Any? = null
// And iterate over all properties
while (jsonReader.hasNext()) {
val data = jsonReader.nextName()
debug("Unwrap Stuff: $data")
when (data) {
"1", "2", "3", "4", "5", "6" -> value = typeAdapter.read(jsonReader)
else ->jsonReader.skipValue()
}
}
// Consume the object end `}`
jsonReader.endObject()
return value
})
}
}
}
companion object {
fun create(gson: Gson): Converter.Factory {
return UnwrappingGsonConverterFactory(gson)
}
private fun needsUnwrapping(annotations: Array<Annotation>): Boolean {
for (annotation in annotations) {
if (annotation is Unwrap) {
return true
}
}
return false
}
}
}
And the interface:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class Unwrap
My data classes are these:
data class ActivityNodes(#SerializedName("equipment_layer") val equipmentLayer: List<EquipmentLayer>,
#SerializedName("data") val data: nodeData)
data class nodeData (#SerializedName("1") val nodeList: List<dataItem>) <-- this is where I need someway to tell SerializedName that the value can be anything from 1 to 6
data class dataItem(#SerializedName("equipment_id") val equipmentId: Int,
#SerializedName("item_quantity") val itemQuantity: Int,
#SerializedName("name") val name: String,
#SerializedName("shortcode") val shortCode: String,
#SerializedName("part_number") val partNumber: String,
#SerializedName("equipment_layer_id") val equipmentLayerId: Int,
#SerializedName("equipment_layer_name") val equipmentLayerName: String,
var isScanned: Boolean = false )
data class EquipmentLayer(#SerializedName("id") val id: Int,
#SerializedName("name") val name: String,
#SerializedName("detail") val details: String)
For the dynamic JSON, you have to parse the JSON string manually. To get JSON string from retrofit you have to use ScalarsConverterFactory instead of GsonConverterFactory.
Add this dependency:
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
Create Adapter like this:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://echo.jsontest.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
Create request method with ResponseBody
public interface MyService {
#GET("/key/value/one/two")
Call<ResponseBody> getData();
}
You can get Json String like this:
MyService service = retrofit.create(MyService.class);
Call<ResponseBody> result = service.getData();
result.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable t) {
e.printStackTrace();
}
});
Now you have to parse the JSON string manually to get your data from JSON.
Hope it helps:)
use below for the part of "data" of json:
Type mapType = new TypeToken<Map<String, List<EqupmentDetail.class>>>() {}.getType(); // define generic type
Map<String, List<EqupmentDetail.class>> result= gson.fromJson(new InputStreamReader(source), mapType);
here define EqipmentDetails class same as your refence
this will definitely work
I have a scenario where I need to duplicate an object (after or before saving it to Parse) only changing one field.
The problem is, I can't figure out how to find the stop condition when saving this new object and the afterSave callback getting called again and again.
My object:
{
"createdAt": "2015-02-21T23:25:03.525Z",
"creator": {
"__type": "Pointer",
"className": "_User",
"objectId": "2k9OzzBrPr"
},
"date": {
"__type": "Date",
"iso": "2015-02-21T22:46:39.048Z"
},
"description": "Hdheha",
"from": {
"__type": "Pointer",
"className": "_User",
"objectId": "Sd9B1XyZVL"
},
"has_accepted": false,
"has_answered": false,
"objectId": "YQCWRo0j2V",
"status": 0,
"to": {
"__type": "Pointer",
"className": "_User",
"objectId": "2k9OzzBrPr"
},
"updatedAt": "2015-02-21T23:25:03.525Z",
"value": 2.3499999046325684
}
My (try) server code:
function saveMirrorDebit(request) {
var toUser = request.object.get("to");
var fromUser = request.object.get("from");
var invertedDebit = request.object;
var Debit = Parse.Object.extend("Debit");
var query = new Parse.Query(Debit);
query.equalTo("parent_debit", {
__type : "Pointer",
className : "Debit",
objectId : invertedDebit.id
});
query.find({
success : function (debit) {
if (debit) {
console.log('debito nao nulo');
} else {
console.log('debito nulo, criando o espelho invertido');
var newDebit = new Debit();
newDebit.set("creator", invertedDebit.get("creator"));
newDebit.set("from", toUser);
newDebit.set("to", fromUser);
newDebit.set("value", -invertedDebit.get("value"));
newDebit.set("parent_debit", {
__type : "Pointer",
className : "Debit",
objectId : invertedDebit.id
});
newDebit.save(null);
}
},
error : function (error) {
console.log(error);
}
});
}
Which is called on afterSave:
Parse.Cloud.afterSave("Debit", function (request) {
...
saveMirrorDebit(request);
...
}
How can I approach this ?
Thanks
First, I wouldn't recommend you duplicating any object... What are you trying to achieve?
Anyhow, in an afterSave, you can achieve what you want. Note that the beforeSave could save you one API call.
Parse.Cloud.afterSave("Debit", function (request) {
var debit = request.object;
if (!(debit.has("duplicated"))){
var Debit = Parse.Object.extend("Debit");
var duplicated_debit = new Debit();
duplicated_debit.set("creator", debit.get("creator"));
duplicated_debit.set("date", debit.get("date"));
duplicated_debit.set("from", debit.get("from"));
// repeat as many times as needed, include your "change logic here"
// that's where the magic happens
duplicated_debit.set("duplicated",true);
debit.set("duplicated",true);
Parse.Object.saveAll([duplicated_debit,debit]);
}
}