I am using the org.json.JSONStringer class to generate JSON strings for my Android application.
fun createJSONString(stringer:JSONStringer) {
stringer.object()
//write some stuff
stringer.endObject()
}
However, I can't call the object() method of this class to create a new JSON Object, since object is a kotlin property. Thus, Android studio tells me that constructors are not allowed for objects. What could I do to force Android Studio to use the JSONStringer's object() method?
Why not use JSONObject class instead, example to and from JSONObject:
val jsonStr = "{\"age\":33,\"messages\":[\"msg 1\",\"msg 2\"],\"name\":\"jim\"}"
val jObj = JSONObject(jsonStr)
val str = jObj.toString()
Since JSONStringer is just an implementation of JSONObject#toString and JSONArray#toString, also usage of this API is discouraged as described in:
JSONStringer
Implements JSONObject#toString and JSONArray#toString. Most application developers should use those methods directly and disregard this API
Here's a good tutorial: Json parser
And as alternative you can use JSON serialization / deserialization library like GSON or Jackson
How to convert Java object to / from JSON (Jackson)
Write
stringer.`object`()
See Escaping for Java identifiers that are keywords in Kotlin.
Related
I am trying to parse the results of an API call which returns a unique first property.
{
"AlwaysDifferent12345": {
"fixedname1" : "ABC1",
"fixedname2" : "ABC2"
}
}
I am using retrofit2 and jackson/gson and cannot figure out how to cope with dynamic property names within the retrofit2 framework. The following works fine
data class AlwaysDifferentDTO(
#JsonProperty("AlwaysDifferent12345") val alwaysDifferentEntry: AlwaysDifferentEntry
)
I have tried
data class AlwaysDifferentDTO(
#JsonProperty
val response: Map<String, AlwaysDifferentEntry>
)
But this returns errors Can not instantiate value of type... The return value from the API is fixed i.e. map<string, object>.
I have read you can write a deserializer but it looks like I need to deserialize the whole object when all I want to do is just ignore the string associated with the response.
I have read
https://discuss.kotlinlang.org/t/set-dynamic-serializedname-annotation-for-gson-data-class/14758
and several other answers. Given unique properties names are quite common it would be nice to understand how people deal with this when using retrofit2
Thanks
Because the JSON doesn't have a 1-to-1 mapping Jackson can't map it automatically using annotations. You are going to need to make your own Deserializer.
In this tutorial you can learn how to create your own custom Deserializer for Jackson. https://www.baeldung.com/jackson-deserialization
In the tutorial you will see the first line under the deserialize function is
JsonNode node = jp.getCodec().readTree(jp);
using this line you can get the JSON node as a whole and once you have it you can call this function
JsonNode AlwaysDifferent12345Node = node.findParent("fixedname1");
Now that you have that node you can retrieve its value like shown in the rest of the tutorial. Once you have all the values you can return a new instance of the AlwaysDifferentDTO data class.
It's my first time dealing with JSON in android studio, and I'm trying to parse the response from google books API to JSON and then retrieve some info about the volume such as the title, author, and the description. the problem is there are JSON objects within the main JSON. Should I create a data class for each JSON?
the API link 'https://www.googleapis.com/books/v1/volumes?q=kotlin'
how i'm parsing it
val jsonResponse = gson.fromJson(body, JsonResponse::class.java)
the data classes that i created
data class JsonResponse
(val kind:String,val count:String,val items:List<JsonItems> )
data class JsonItems
(val kind:String,
val id:String,
val etag:String,
val self:String,
val volumeInfo:List<VolumeInfo>,
val saleInfo:List<SaleInfo>,
val accessInfo:ListList<AccessInfo> ,
val searchInfo:List<SearchInfo>)
is there any simpler solution to avoid unused data classes?
You need to have these classes to be able to parse the json, but then you can just create objects of other classes using those data (and drop the earlier instances).
Also, you don't need to include fields that you don't need.
You can parse without data classes and you can use other tools than Gson.
I had a use case similar to yours. I did not want to write data classes, but just get direct access to the specific data I was interested in from a larger nested JSON object.
I found a solution that worked well for me here
https://johncodeos.com/how-to-parse-json-in-android-using-kotlin/
using JSONTokener https://developer.android.com/reference/org/json/JSONTokener
Based on these examples, some code for parsing books might look like:
// response is a String with the JSON body
val jsonObject = JSONTokener(response).nextValue() as JSONObject
val items = jsonObject.getString("items") as JSONArray
for (i in 0 until items.length()) {
val volumeInfo = items.getJSONObject(i).getString("volumeInfo")
val title = volumeInfo.getString("title")
}
Another Stackoverflow post with other suggestions is here How to parse JSON in Kotlin?
I was wondering if somewhere out there exists a java library able to query a JSONObject. In more depth I'm looking for something like:
String json = "{ data: { data2 : { value : 'hello'}}}";
...
// Somehow we managed to convert json to jsonObject
...
String result = jsonObject.getAsString("data.data2.value");
System.out.println(result);
I expect to get "hello" as output.
So far, the fastest way I have found is using Gson:
jsonObject.getAsJsonObject("data").getAsJsonObject().get("data2").getAsJsonObject("value").getAsString();
It's not actually easy to write and read. Is there something faster?
I've just unexpectedly found very interesting project: JSON Path
JsonPath is to JSON what XPATH is to XML, a simple way to extract parts of a given document.
With this library you can do what you are requesting even easier, then my previous suggestion:
String hello = JsonPath.read(json, "$.data.data2.value");
System.out.println(hello); //prints hello
Hope this might be helpful either.
While not exactly the same, Jackson has Tree Model representation similar to Gson:
JsonNode root = objectMapper.readTree(jsonInput);
return root.get("data").get("data2").get("value").asText();
so you need to traverse it step by step.
EDIT (August 2015)
There actually is now (since Jackson 2.3) support for JSON Pointer expressions with Jackson. So you could alternatively use:
return root.at("/data/data2/value").asText();
First of all, I would recommend consider JSON object binding.
But in case if you get arbitrary JSON objects and you would like process them in the way you described, I would suggest combine Jackson JSON processor along with Apache's Commons Beanutils.
The idea is the following: Jackson by default process all JSON's as java.util.Map instances, meanwhile Commons Beanutils simplifies property access for objects, including arrays and Map supports.
So you may use it something like this:
//actually it is a Map instance with maps-fields within
Object jsonObj = objectMapper.readValue(json, Object.class);
Object hello = PropertyUtils.getProperty(jsonObj, "data.data2.value")
System.out.println(hello); //prints hello
You can use org.json
String json = "{ data: { data2 : { value : 'hello'}}}";
org.json.JSONObject obj = new org.json.JSONObject(json);
System.out.println(obj.query("/data/data2/value"));
I think no way.
Consider a java class
class Student {
Subject subject = new Subject();
}
class Subject {
String name;
}
Here if we want to access subject name then
Student stud = new Student();
stud.subject.name;
We cant access name directly, if so then we will not get correct subject name. Like here:
jsonObject.getAsJsonObject("data")
.getAsJsonObject()
.get("data2")
.getAsJsonObject("value")
.getAsString();
If you want to use same like java object then use
ClassName classObject = new Gson().fromJson(JsonString, ClassName.class);
ClassName must have all fields to match jsonstring. If you have a jsonobject inside a jsonobject then you have to create separate class like I'm doing in Student and Subject class.
Using Java JSON API 1.1.x (javax.json) one can make use of new JavaPointer interface. Instance implementing this interface can be considered to some extend as kind of XPath expression analog (see RFC-6901 for details). So in your case you could write this:
import javax.json.*;
//...
var jp = Json.createPointer("/data/data2/value");
System.out.println(jp.getValue(jsonObject));
In 1.1.4 version of JSON there's also nice addition to JsonStructure interface (which is implemented by JsonObject and JsonArray), namely getValue(String jsonPointer). So it all comes down to this simple one-liner:
System.out.println(jsonObject.getValue("/data/data2/value"));
I am working with socket.io library which emits messages to the socket.io server. The server expects JSON objects, arrays, etc. My original implementation used JSONOject and JSONArray datatypes. However, I would like to switch to using classes generated via Parceler library. The classes generated with library's annotations can be wrapped into Parcels. It seems like a very convenient way of managing such communication. However, is there a way to convert Parceler's class or a Parcel class into a JSON string or JSONObject/Array?
GSON library supports toJson method and I know that Retrofit does some magic with Parcels, JSON, and GSON.
i guess this will help you,
Create a class with getters and setters method for example
class A
{
int b;
void setb(int x){this.b = x;}
int getb(){return this.b}
}
than you can create json from the object of this class:
new Gson().toJson(a)
Or object from json:
a = new Gson().fromJson(data, A.class);
I'm currently using GSON to parse my JSON to Objects. I was using the standard way like :
Result response= gson.fromJson(reader, Result.class);
Result can be a very complex object with other Complex objects, with up to 5 levels of complex objects. But I have no issues with that.
My Question is : I would like to be able to have in some objects an attribute with a flexible type.
For example :
class Class1 {
String hello;
}
class Class2 {
String world;
}
class Class3 {
Class<?> (= class1 or class2) hello;
}
// Parsing time
Class<?> response= gson.fromJson(reader, Class3.class);
try {
Class1 ret = (Class1)response;
} catch ... {
Class2 ret = (Class2)response;
}
Hope it's clear enough.
Unfortunately, the latest release of Gson (2.0) still doesn't have built-in support for an easy configuration to provide polymorphic deserialization. So, if Gson must be used (instead of an API that has such built-in support, like Jackson -- using which I've posted complete examples for polymorphic deserialization at http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html), then custom deserialization processing is necessary.
For deserialization to polymorphic types, something in the JSON must be present to identify which concrete type to deserialize to.
One approach would be to have an element in the JSON dedicated to just this purpose, where the deserialization code selects the correct type based on the value of the special-purpose element. For example:
{"type":"Class1","hello":"Hi!"} --> deserializes to Class1 instance
{"type":"Class2","world":"Earth"} --> deserializes to Class2 instance
Another approach would be to just switch on the presence of particular JSON element names, though instead of try-catch blocks as demonstrated in the original question, I'd just use if-statements.
See Gson issue 231 for more on this topic, as well as possible information on when a built-in polymorphic deserialization facility might be included in Gson.
Another StackOverflow.com post with an example of polymorphic deserialization with Gson is Polymorphism with gson