Parse json in Kotlin - json

I have json like:
{
"member": [
{
"name": "string",
"lastname": "string",
"email": "string",
"address": {
"city": "string",
"country": "string"
}
}
]
}
I parse with the fromGson(google)
Gson().fromJson(jsonString,Member::class.java)
I already have a class to map this json on each field
data class Member(
var name: String,
var lastname: String,
var email: String,
var address: HashMap<String, String>
)
but I have problem to map address, I don't know why
it's only work when json like
"address":
{
"city": "string"
}
it look like I need parse map on json object whos in json object too
array of hashmap didn't work also

Probably you need a wrapper class on the top of member class, try this:
private val jsonString =
"{\"member\": [{ \"name\": \"string\",\"lastname\": \"string\",\"email\": \"string\",\"address\": { \"city\": \"string\",\"country\": \"string\" } }]}"
data class Member(
var name: String,
var lastname: String,
var email: String,
var address: HashMap<String, String>
)
class Response(val member: List<Member>)
fun parse() {
val response = Gson().fromJson(jsonString, Response::class.java)
for (member in response.member){
// do something
}
}

Related

Kotlin: How to serialize a interface and ignore the class attributes

interface MyInterface {
fun schemaName(): String
fun data(): Any
}
data class Object1(
val name: String,
val title: String,
val description: String,
val list: List<String>
): MyInterface {
override fun schemaName(): String = "SCHEMA_FOR_OBJECT1"
override fun data(): Any = this
}
With this MyInterface and Object1, Kotlin serializes my Object1 into
{
"name": xxx,
"title": xxxx,
"description": xxxx,
"list": [
"xxxx",
"xxxx"
]
}
This is understandable but is there anyway I can serialize Object1 into
{
"schemaName": "SCHEMA_FOR_OBJECT1",
"data": {
{
"name": xxx,
"title": xxxx,
"description": xxxx,
"list": [
"xxxx",
"xxxx"
]
}
}
}
I want to have multiple objects which implement MyInterface but I want them all to serialize into a json with common parent keys.

Read large (+- 50Mb) Json file using Kotlin

I'm starting to work on a weather app using "openweathermap.org" API, and they provide you with a list of available cities in Json format.
Before i continue with the project, i would like to able to work with the data from this Json file.
The problem is that i get Null whenever i try to read and parse that file.
Here is the code:
Main Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val jsonFileString = getJsonDataFromAsset(applicationContext, "citylist.json")
Log.i("gabs Data", jsonFileString ?: "Empty Data")
val gson = Gson()
val listOfCities = object : TypeToken<List<CityList>>() {}.type
var cities: List<CityList> = gson.fromJson(jsonFileString, listOfCities)
cities.forEachIndexed { idx, city -> Log.i("data", "> Item $idx:\n$city") }
}
}
Utils.kt:
fun getJsonDataFromAsset(context: Context, fileName: String): String? {
val jsonString: String
try {
jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() }
} catch (ioException: IOException) {
ioException.printStackTrace()
return null
}
return jsonString
}
And the data class (Array of cities data):
class CityList : ArrayList<CityList.CityListItem>(){
data class CityListItem(
#SerializedName("coord")
val coord: Coord,
#SerializedName("country")
val country: String,
#SerializedName("id")
val id: Double,
#SerializedName("name")
val name: String,
#SerializedName("state")
val state: String
) {
data class Coord(
#SerializedName("lat")
val lat: Double,
#SerializedName("lon")
val lon: Double
)
}
}
And the error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.weatherdisplay/com.example.weatherdisplay.ui.activities.MainActivity}: java.lang.NullPointerException: gson.fromJson(jsonFileString, listOfCities) must not be null.
Caused by: java.lang.NullPointerException: gson.fromJson(jsonFileString, listOfCities) must not be null
at com.example.weatherdisplay.ui.activities.MainActivity.onCreate(MainActivity.kt:21)
There were some problems in your code:
You were not closing the BufferedReader
You should not load the file on the Main thread since it will block the UI
I created some sample data corresponding to your data structure:
[
{
"id": 1,
"country": "Germany",
"state": "Saxony",
"name": "Dresden",
"coord": {
"lat": 0.0,
"lon": 0.0
}
},
{
"id": 2,
"country": "Germany",
"state": "Berlin",
"name": "Berlin",
"coord": {
"lat": 0.0,
"lon": 0.0
}
},
{
"id": 3,
"country": "Germany",
"state": "Baden-Wuerttemberg",
"name": "Stuttgart",
"coord": {
"lat": 0.0,
"lon": 0.0
}
},
{
"id": 4,
"country": "Germany",
"state": "Hessen",
"name": "Frankfurth",
"coord": {
"lat": 0.0,
"lon": 0.0
}
},
{
"id": 5,
"country": "Germany",
"state": "Nordrhine-Westphalia",
"name": "Cologne",
"coord": {
"lat": 0.0,
"lon": 0.0
}
}
]
Your activity:
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MyApplication"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launchWhenStarted {
launch(Dispatchers.IO) {
var reader: BufferedReader? = null
try {
// Create a reader and read the file contents
reader = assets.open("data.json").bufferedReader()
val rawData = reader.use { it.readText() }
// Create a Type token that Gson knows how to parse the raw data
val cityListType = object : TypeToken<List<City>>() {}.type
// Parse the raw data using Gson
val data: List<City> = Gson().fromJson(rawData, cityListType)
// TODO: Do something with the data
} catch (e: IOException) {
// Handle IOException: Gets thrown when the file wasn't found or something similar
Log.e(TAG, "An error occurred while reading in the data:", e)
} catch (e: JsonParseException) {
// Handle JsonParseException: Gets thrown when there is a problem with the contents of the file
Log.e(TAG, "An error occurred while reading in the data:", e)
}
finally {
// Close the reader to release system resources
reader?.close()
}
}
}
}
}
Your data structure:
data class City(
#SerializedName("id")
val id: Int,
#SerializedName("country")
val country: String,
#SerializedName("state")
val state: String,
#SerializedName("name")
val name: String,
#SerializedName("coord")
val coordinate: Coordinate
) {
override fun toString(): String {
return "#$id[$name $state $country]#[${coordinate.lat}|${coordinate.lon}]"
}
}
data class Coordinate(
#SerializedName("lat")
val lat: Double,
#SerializedName("lon")
val lon: Double
)
In the best case you would put the code in which you get the file contents and parse the data in a ViewModel, but this would to go beyond the scope for this answer.
Additional information about ViewModels: https://developer.android.com/topic/libraries/architecture/viewmodel

How to create a Swift model for JSON

{"dataList":{"1547795650562": {
"c0a8007b-6759-111d-8167-59e8dabe0086": {
"recordDate": 1547795650562,
"resultValue": "160",
"vitalParameter": {
"uom": {
"code": "KG",
"name": "KG",
"id": "c0a8007b-6759-111d-8167-59e76204007f"
},
"resultType": {
"code": "VSRTNUMERIC",
"name": "Numeric",
"id": "20cf4756-40b0-4cc1-acb5-861765370a41"
},
"code": "29463-7",
"name": "Weight",
"id": "c0a8007b-6759-111d-8167-59e8dabe0086"
},
"id": "c0a8007b-6855-1d16-8168-5fd18fa301b7"
}}
}}
getting 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 as class names. But I dont want like this;
class DataList : NSObject, NSCoding{
var 1547795650562 : 1547795650562!
}
class 1547795650562 : NSObject, NSCoding{
var c0a8007b6759111d816759e8dabe0086 : VitalParameter!
}
But the problem here is, 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 cannot be hard coded because they may change.
c0a8007b-6759-111d-8167-59e8dabe0086 is dynamic id and 1547795650562 is recordDate. Inner object is repetitive.
But I have to map as the keys are of recordDate and id respectively.
Try using Codable instead of NSCoding to parse your JSON data.
Models:
struct Root: Codable {
let dataList: [String:[String:Record]]
}
struct Record: Codable {
let recordDate: Int
let resultValue: String
let vitalParameter: VitalParameter
let id: String
}
struct VitalParameter: Codable {
let uom, resultType: ResultType
let code, name, id: String
}
struct ResultType: Codable {
let code, name, id: String
}
Parse the JSON data using above models like,
do {
let response = try JSONDecoder().decode(Root.self, from: data)
print(response)
} catch {
print(error)
}
Note: You can use https://app.quicktype.io to get the models from your JSON instantly. Make the changes as per your requirement and you're good to go.

Can't handle json tree list using TypeToken + generics with Gson in Kotlin

I have got the json structure as below:
{
"object": "list",
"total": 3,
"data": [
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
},
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
},
{
"object": "brand",
"id": "15243937043340",
"company": {
"object": "company",
"id": "956936000",
"name": "ABC"
},
"name": "Kindle",
"images": [
"http://www.spacecentrestorage.com/assets/uploads/General/SCS-Slide02-Commercial.jpg"
]
}
],
"associated": {}
}
And this is my Gson data class mapping :
data class Response (
#SerializedName("object")
val obj: String,
val total: Int,
val data: List<*>,
val associated: Response
)
data class Brand (
#SerializedName("object")
val obj: String,
val id: String,
val name: String,
val images: List<String>,
val company: Company
)
data class Company (
#SerializedName("object")
val obj: String,
val id: String,
val name: String
)
When it comes to extracting the tree as above, I find returned data string becomes Malformed Json and gives MalformedJsonException on $[0].companies.null
I have read about the recursive deserialisation function but it is not working in my case. I resort to deserialise as below , using original method, it causes errors
val response = gson.fromJson(queryResult , Response::class.java)
println("result 2 : $response" )
val dataString = response.data.toString()
println("result 3 : $dataString" )
val brands = Gson().fromJson(dataString, Array<Brand>::class.java).toMutableList()
println("result 4 : $brands" )
I would like to ask :
If returning json component to string, shall all the indents and symbols " be erased ?
To extract all associated object of the elements of the list of objects, what precautions do I have to take for deserialising list of objects using Gson ?
If you set the type parameter of the data list in Response to Brand GSON knows how to deserialise the items of the list.
data class Response (
#SerializedName("object")
val obj: String,
val total: Int,
val data: List<Brand>,
val associated: Response
)
Using this there is no need to parse the items of the list again and you can get all brands like this:
val response = Gson().fromJson(queryResult , Response::class.java)
val dataList = response.data
print("brands: " )
dataList.forEach { println(it) }

How to persist an array using Realm Swift and ObjectMapper?

I get an error when I try to save an array that comes from a JSON string. I've tried to use RLMArray with no success.
The error I receive is:
'RLMException', reason: 'Property 'page' is of type 'RLMArray<(null)>' which is not a supported RLMArray object type.
My model class:
public class Project: Object, Mappable {
dynamic var id = 0
dynamic var user: User!
dynamic var page: RLMArray!
dynamic var error_message: String! = ""
dynamic var status: String! = ""
override public static func primaryKey() -> String? {
return "id"
}
required convenience public init?(_ map: Map) {
self.init()
mapping(map)
}
public func mapping(map: Map) {
user <- map["user"]
page <- map["page"]
error_message <- map["error_message"]
status <- map["status"]
}
}
JSON File:
let parameters = [
"user": [
"username": "Marcus",
"password": "123asd"
],
"page": [
"home": [
[
"kind": "navigation",
"title": "suite",
"image": "ic_suite",
"backgroundImage": "ic_background1"
],
[
"kind": "navigation",
"title": "jardim",
"image": "ic_jardim",
"backgroundImage": "ic_background2"
]
],
"suite": [
[
"kind": "button",
"title": "My Master Suite",
"textColor": "0x000000",
"textSize": "16"
]
]
],
"status": "success",
"error_message": ""
]
self.project = Mapper<Project>().map(parameters)
Your class inherits from Object, Realm Swift's base class, but is attempting to use RLMArray, a Realm Objective-C type, in its interface. You cannot mix Realm Swift and Realm Objective-C in this manner. You should use List<T> for array properties when using Realm Swift.