Is it possible to get JsonObjects or strings in Json format when using DataSnapshot.getValue()? Maybe I wasn't thorough enough with my search, but I couldn't find a way to use a custom serializer.
Update:
I haven't had the time for checking out the documentation for the latest SDK but it seems like there are some options for JSON fields.
The getValue() in DataSnapshot returns a HashMap.
So in any method of your Firebase listener, you could:
Get the value of the DataSnapshot, which will return a HashMap:
HashMap<String, JSONObject> dataSnapshotValue = (HashMap<String, JSONObject>) dataSnapshot.getValue();
Cast the Hashmapto a valid JSON string, using Gson:
String jsonString = new Gson().toJson(dataSnapshotValue);
With this valid jsonString, cast it as needed:
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(User.class, new UserSerializer());
final Gson gson = gsonBuilder.create();
User parsedUser = gson.fromJson(jsonString, User.class);
This gives you the possibility to use anything you want to cast an object from Firebase, you could either use custom serializers, an ExclusionStrategy, or retrieve some values from the HashMap directly.
Based on #RominaV answer and in case it may be helpful to someone that's what I have in Kotlin:
myRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
adapter.clear()
dataSnapshot.children.forEach {
val cardMap = it.value
val json = Gson().toJson(cardMap)
val card = Gson().fromJson<Card>(json, Card::class.java)
adapter.add(CardItem(card))
}
}
And that's my Card class
data class Card(var courseId: String, var cardQuestion: String, var cardAnswer: String) {
constructor() : this("", "", "")
}
I can also get the same outcome traversing through the dataSnapshot children this way:
dataSnapshot.children.forEach {
adapter.clear()
val courseId = it.child("courseId").value as String
val question = it.child("cardQuestion").value as String
val answer = it.child("cardAnswer").value as String
adapter.add(CardItem(Card(courseId, question, answer))
}
Thanks. :)
Related
I have an JSONObject which is creating in the following way.
For a special reason, I have to convert it to Gson in this way, as i can modify only extractDataToString() method. But I do not get proper value after conversion.
fun createJsonString() {
val headerJson = JSONObject()
headerJson.put("typ1", "value1")
headerJson.put("typ2", "value2")
extractData(headerJson.toString())
}
fun extractDataToString(jsonString: String) {
val headerJson = Gson().fromJson(jsonString, JsonObject::class.java)
val resultType1 = headerJson.remove("typ1")?.toString() // Here, resultType1 becomes
"value1", but it should be
value1 that is extra qutomation
mark is being added.
}
Why extra quotation mark is being added? Can anyone please help? I am a newbie.
Call
.getAsString()
Instead of
toString()
at the end
ref : docs
I have response like this :
{
"response":{"numFound":5303,"start":0,"maxScore":6.5102634,"docs":[
{
"id":"10.1371/journal.pone.0000290",
"journal":"PLoS ONE",
"eissn":"1932-6203",
"publication_date":"2007-03-14T00:00:00Z",
"article_type":"Research Article",
"author_display":["Rayna I. Kraeva",
"Dragomir B. Krastev",
"Assen Roguev",
"Anna Ivanova",
"Marina N. Nedelcheva-Veleva",
"Stoyno S. Stoynov"],
"abstract":["Nucleic acids, due to their structural and chemical properties, can form double-stranded secondary structures that assist the transfer of genetic information and can modulate gene expression. However, the nucleotide sequence alone is insufficient in explaining phenomena like intron-exon recognition during RNA processing. This raises the question whether nucleic acids are endowed with other attributes that can contribute to their biological functions. In this work, we present a calculation of thermodynamic stability of DNA/DNA and mRNA/DNA duplexes across the genomes of four species in the genus Saccharomyces by nearest-neighbor method. The results show that coding regions are more thermodynamically stable than introns, 3′-untranslated regions and intergenic sequences. Furthermore, open reading frames have more stable sense mRNA/DNA duplexes than the potential antisense duplexes, a property that can aid gene discovery. The lower stability of the DNA/DNA and mRNA/DNA duplexes of 3′-untranslated regions and the higher stability of genes correlates with increased mRNA level. These results suggest that the thermodynamic stability of DNA/DNA and mRNA/DNA duplexes affects mRNA transcription."],
"title_display":"Stability of mRNA/DNA and DNA/DNA Duplexes Affects mRNA Transcription",
"score":6.5102634},
Now in this I want to get the 'abstract' field. For this I had specified it as String but it gave me error that it the array and can not convert to string.
Now I am not sure how to create object for this which array type I should specify.
I checked that we can use the Type Converters but not able to write the converter for the same.
Following is my object and converter which I tried.
DAO
#Entity(tableName = "news_table")
data class NewsArticles(
#PrimaryKey var id: String = "",
#SerializedName("article_type") var title: String? = null,
#SerializedName("abstract") var description: Array<String>,
#SerializedName("publication_date") var publishedAt: String? = null
)
Type Converter
class Converters {
#TypeConverter
fun fromTimestamp(value: Array<String>?): String? {
return value?.let { String(it) } //error
}
#TypeConverter
fun dateToTimestamp(array: Array<String>): String? {
return array.toString()
}
}
Its giving me error for return line that none of the following functions can be called with arguments supplied.
EDIT :
now I changed defination to ArrayList
#SerializedName("abstract") var description: ArrayList,
and converter to this
class ArrayConverters {
#TypeConverter
fun fromArray(value: ArrayList<String>?): String? {
return value?.let { arrayToString(it) }
}
#TypeConverter
fun arrayToString(array: ArrayList<String>): String? {
return array.toString()
}
}
Now its showing this error : error: Multiple methods define the same conversion. Conflicts with these: CustomTypeConverter
Please help. Thank you.
EDIT 2:
As per answer of richard slond, I have added the converter as
class ArrayConverters {
#TypeConverter
fun to(array: Array<String>): String {
return array.joinToString(" ")
}
#TypeConverter
fun from(value: String): List<String> {
return value.split(" ")
}
}
and added in the database as
#Database(entities = [NewsArticles::class], version = 2, exportSchema = false)
#TypeConverters(ArrayConverters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun newsArticlesDao(): NewsArticlesDao
}
Also in the news article module
#Entity(tableName = "news_table")
#TypeConverters(ArrayConverters::class)
data class NewsArticles(
#PrimaryKey var id: String = "",
#SerializedName("article_type") var title: String? = null,
#SerializedName("abstract") var description: String? = null,
#SerializedName("publication_date") var publishedAt: String? = null
)
Here for descriptionn variable if i have added string I am getting error as the field is begin with array.
and if i have specified as the arraylist it gives the error as can not add this type to the database please try using type converter.
What's missing??
The easiest way to store Collection (like Array, List) data into database is to convert them to String In JSON format, and GSON library (developed by Google) is designed for this situation.
How to Use:
String jsonString;
toJsonButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Gson gson = new Gson();
jsonString = gson.toJson(student); //object -> json
}
});
toObjectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Gson gson = new Gson();
Student currentStudent = gson.fromJson(jsonString, Student.class); //json -> object
}
});
Reminder: try to put your Collection into an Object (as a member variable), otherwise you may need following extra works:
Get the Type for Your Collection:
TypeToken<List<Integer>> typeToken = new TypeToken<List<Integer>>(){};
List<Integer> retrievedNumbers = gson.fromJson(numbersJsonString, typeToken.getType());
If you really want to do that, you need to find a way to represent an array of strings using a primitive type. The easiest way is to use JSON format. For that reason, in your converter, you need to serialize and deserialize your string array.
As quick solution (which I do not recommend) is the following:
#TypeConverter
fun to(array: Array<String>): String {
return array.joinToString(",")
}
#TypeConverter
fun from(value:String): Array<String> {
return value.split(",")
}
Please be aware, following this path, your strings cannot include commas - but you can use another not so common character as separator
In my application, I use this class as a model:
class ExpenseItem (val concept: String, val amount: String, val months: List<String>, val type: Type, val cards_image: Int, val payDay: Int, val notes: String) {
enum class Type {RECURRENT, VARIABLE}
}
And with this model, I create a mutable list
var generalExpensesList: MutableList<ExpenseItem> = mutableListOf()
and I add items
val currentExpense = ExpenseItem(
concept,
amount,
listOfMonths,
enumtype,
card_image_number,
payday.toInt(),
notes
)
generalExpensesList.add(currentExpense)
As you can see, one of the model fields is also a String type list, in case it was important
Well, my intention is to convert this list to String, save it as a sharedpreference, and then create a new list using that String retrieved from the sharedpreference.
To convert the list to String I can use toString or joinToString, both give me an optimal result.
I have the problem when I want to create a new list from the String.
I can do it with lists of type List<String>, but never with lists of type List<ExpenseItem>
Can someone help me with this?
Simple way, you can use Gson library, add it to build.gradle, it will serialize your list to JSON and save it to SharePreference
implementation 'com.google.code.gson:gson:2.8.6'
public void saveItems(List<ExpenseItem> items) {
if (items != null && !items.isEmpty()) {
String json = new Gson().toJson(items);
mSharedPreferences.edit().putString("items", json).apply();
}
}
public List<ExpenseItem> getItems() {
String json = mSharedPreferences.getString("items", "");
if (TextUtils.isEmpty(json)) return Collections.emptyList();
Type type = new TypeToken<List<ExpenseItem>>() {
}.getType();
List<ExpenseItem> result = new Gson().fromJson(json, type);
return result;
}
You need to use Gson() class.
First Make class like below
class WhateverName(var generalExpensesList: MutableList<ExpenseItem> = mutableListOf())
and pass your list to this class and make a object of this class and then need to make string of it like below
Gson().toJson(WhateverName(arrayListOf()))
it will give you the string and save it to preference as a string.
After retrieve string from preference you need convert it to that object again for doing that use below code.
Gson().fromJson("string", WhateverName::class.java)
it will give you the class of WhateverName, you can access your list from it.
I wrote a Java App where data was retrieved from an API in JSON. Depending on the endpoint the actual data could be very divers. So I made a function to convert the data to the required class for every possible class. This gives me multiple functions which only differ in the classtype to convert to. Following are two examples:
public List<ZermeloBranch> processBranches(ZermeloApiResponseObject responseObject) {
List<ZermeloBranch> branches = new ArrayList<>();
List<LinkedTreeMap> branchMaps = responseObject.getData();
Gson gson = new Gson();
for (LinkedTreeMap treeMap : branchMaps) {
branches.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloBranch.class));
}
return branches;
}
public List<ZermeloAnnouncement> processAnnouncements(ZermeloApiResponseObject responseObject) {
List<ZermeloAnnouncement> announcements = new ArrayList<ZermeloAnnouncement>();
List<LinkedTreeMap> announcementMaps = responseObject.getData();
Gson gson = new Gson();
for (LinkedTreeMap treeMap : announcementMaps) {
announcements.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloAnnouncement.class));
}
return announcements;
}
Now I am rewriting this app to Kotlin and I summise it should be possible to write one function to process the data passing the class to decode to as a parameter. So I made both ZermeloBranch and ZermeloAnnouncement inherit from ZermeloDataObject. I would like to write one function like this:
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
val gson = Gson()
for (item in 0 until data.length()){
returnList.add(gson.fromJson(item, convertToClass))
}
return returnList
}
and call it with processDataToList(data, ZermeloAnnouncements::class) and get a List<ZermeloAnnoucement> returned of call it with processDataToList(data, ZermeloBranch::class) and get a List<ZermeloBranch> returned.
Alas, the compiler gives me an error on gson.fromJson stating "None of the following functions can be called with the arguments supplied" and then it lists all possible functions.
Is it possible to use one function as I propose, and if so, what am I doing wrong?
You could use TypeToken provided by Gson to correctly handle the return type. Besides this you are passing index instead of data.
val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):ArrayList<KClass<out ZermeloDataObject>>{
val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type
val gson = Gson()
for (item in 0 until data.length()){
returnList.add(gson.fromJson(data[item].toString(), type))
}
return returnList
}
Instead of this you could do the whole thing in a single line of code. Check below:
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
return Gson().fromJson(data.toString(), object: TypeToken<List<KClass<out ZermeloDataObject>>>() {}.type)
}
Beside this, you can also use below style which is more generic:
fun <T> processDataToList(data:JSONArray): List<T> {
val type = object: TypeToken<T>() {}.type
val returnList: ArrayList<T> = arrayListOf()
val gson = Gson()
for (item in 0 until data.length()) {
returnList.add(gson.fromJson(json[item].toString(), type))
}
return returnList
}
Or Simply
fun <T> processDataToList(data:JSONArray):List<T>{
return Gson().fromJson(data.toString(), object: TypeToken<List<T>>() {}.type)
}
I'm really new in programming, and recently started a project in Kotlin with Android Studio.
So, I have a problem with a JSON object. I get data from an BroadcastReceiver object, a String to be more specific, with the next format:
{"s1":1}
This, is a simple string. So I took in a function call toJson and I do this.
private fun toJson(data:String): JSONObject {
var newData: String = data.replace("\"","")
newData = newData.replace("{","")
newData = newData.replace("}","")
val newObject = newData.split(":")
val name = newObject[0]
val value = newObject[1]
val rootObject = JSONObject()
rootObject.put(name,value)
return rootObject
}
Im doing this the right way?, how can I improve my code?
Thanks for your help, and sorry for my english!
Welcome to StackOverflow!
In 2019 no-one is really parsing JSON manually. It's much easier to use Gson library. It takes as an input your object and spits out JSON string and vice-versa.
Example:
data class MyClass(#SerializedName("s1") val s1: Int)
val myClass: MyClass = Gson().fromJson(data, MyClass::class.java)
val outputJson: String = Gson().toJson(myClass)
This way you're not working with JSON string directly but rather with Kotlin object which is type-safe and more convenient.
Look at the docs. It's pretty big and easy to understand
Here is some tutorials:
https://www.youtube.com/watch?v=f-kcvxYZrB4
http://www.studytrails.com/java/json/java-google-json-introduction/
https://www.tutorialspoint.com/gson/index.htm
UPDATE: If you really want to use JSONObject then use its constructor with a string parameter which parses your JSON string automatically.
val jsonObject = JSONObject(data)
Best way is using kotlinx.serialization. turn a Kotlin object into its JSON representation and back marking its class with the #Serializable annotation, and using the provided encodeToString and decodeFromString<T> extension functions on the Json object:
import kotlinx.serialization.*
import kotlinx.serialization.json.*
#Serializable
data class User(val name: String, val yearOfBirth: Int)
// Serialization (Kotlin object to JSON string)
val data = User("Louis", 1901)
val string = Json.encodeToString(data)
println(string) // {"name":"Louis","yearOfBirth":1901}
// Deserialization (JSON string to Kotlin object)
val obj = Json.decodeFromString<User>(string)
println(obj) // User(name=Louis, yearOfBirth=1901)
Further examples: https://blog.jetbrains.com/kotlin/2020/10/kotlinx-serialization-1-0-released/
I am adding 3 templates here for Kotlin Developers, It will solve json converting & parsing problems.
//Json Array template
{
"json_id": "12.4",
"json_name": "name of the array",
"json_image": "https://image_path",
"json_description": "Description of the Json Array"
}
Kotlin Model class
data class JsonDataParser(
#SerializedName("json_id") val id: Long,
#SerializedName("json_name") val name: String,
#SerializedName("json_image") val image: String,
#SerializedName("json_description") val description: String
)
Converting to Json String from the Model Class
val gson = Gson()
val json = gson.toJson(jsonDataParser)
Parsing from Json file/Strong
val json = getJson()
val topic = gson.fromJson(json, JsonDataParser::class.java)