Is it possible to parse string using GSON and convert values like '1','on','yes' as booleans true?
So, I have class:
public class OItem {
public int id;
public String name;
public Boolean is_online;
}
And then I need using GSON translate it to OItem object
String string_json = "{id:200, name: Ivan Ivan, is_online : yes}";
Gson gson = new Gson();
OInfo = gson.fromJson(string_json, OInfo.class);
How to make so that some values will be treated as true, and others as false?
Thank you.
That's not valid JSON. {id:200, name: "Ivan Ivan", is_online : "yes"} is valid but the name and is_online values are strings.
JSON is typed - all values are Strings, Numbers, Booleans, Objects or Lists and so that's what any (de)serializer will expect. Anything else will not be JSON and therefore you won't be able to use a JSON parser to parse it.
What you want to do is have getters in OItem that check the String value against a list of "true" values and return a normal boolean instead.
Related
Seen this thread for possible solutions but it beats me.
I store a customer data object as a JSON String in shared preferences (Android) and try to retrieve the string convert back to customer object and use for validations. The conversion from object to JSON String occurs perfectly well and stores in preferences (as a key value(string) pair).
When I try to create a JSONObject or an array (Its not an array but tried anyway) using the SharedPreferences.getString(key,"") - I always get the same error "Value ... of type java.lang.String cannot be converted to JSONObject".
I am hoping a different pair of eyes catch something I could not.
Error message with data (masked):
Value
{"address":{"city":"city","country":"country","customer_address_id":0,"customer_id":0,"house_number":"#123, Lane 1, Street 1","latitude":0.0,"longitude":0.0,"postcode":"12001","street":"Lane 2, Street 2"},"ageGroup":"25-45","dateOfBirth":"1537308474000","email":"abc#abc.com","firstName":"abcdefg","gender":"","id":"108","lastName":"xyz","locale":"en_us","middleName":"none","phone":"1234567890","uuid":"8c3ce2c5-600f-3c4e-bc07-727d61fae7ff"}
of type java.lang.String cannot be converted to JSONObject.
All am trying to do is (gist of code):
Saving to shared prefs using below:
jsonAdapter = moshi.adapter(RegisterUser.class);
mRegisterUser = (RegisterUser) jsonAdapter.fromJson(regisCustUser.toString());
prefs = UserPrefs.getUserPrefs(getApplicationContext());
prefs.setPrefsItem(UserPrefs.getregUserKey(), jsonAdapter.toJson(mRegisterUser));`
where setPrefsItem does below:
JsonAdapter jsonAdapter = moshi.adapter(javaObject.getClass());
String cartJson = jsonAdapter.toJson(javaObject);
// Log.e("CartJsonreflection", cartJson);
editor.putString(prefKey, cartJson);
editor.commit();
Here is where retrieve occurs:
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
...
private static SharedPreferences settings;
...
public static Object someMethod(String prefKey, Object javaObject) {
String prefJson = settings.getString(prefKey, "");
JSONObject regisCustomer = new JSONObject(prefJson);
javaObject = (someObject) jsonAdapter.fromJson(regisCustomer.toString());
// casting is redundant but added for clarity
return javaObject;
}
Where prefJson has the above string.
I have tried to validate the JSON string on two websites - https://jsoneditoronline.org/ and https://jsonlint.com/
I have even pasted the string in Notepad++ for any special characters but found none.
Might be the case that you are trying to convert the empty string which the default string to JSONObject .
Make sure you are getting a value in prefJson by , making sure that you have initialized the SharedPreferenceswith the correct key and the key for the getString() is also right.
var s ="{\"address\":{\"city\":\"city\",\"country\":\"country\",\"customer_address_id\":0,\"customer_id\":0,\"house_number\":\"#123, Lane 1, Street 1\",\"latitude\":0.0,\"longitude\":0.0,\"postcode\":\"12001\",\"street\":\"Lane 2, Street 2\"},\"ageGroup\":\"25-45\",\"dateOfBirth\":\"1537308474000\",\"email\":\"abc#abc.com\",\"firstName\":\"abcdefg\",\"gender\":\"\",\"id\":\"108\",\"lastName\":\"xyz\",\"locale\":\"en_us\",\"middleName\":\"none\",\"phone\":\"1234567890\",\"uuid\":\"8c3ce2c5-600f-3c4e-bc07-727d61fae7ff\"}";
val regisCustomer = JSONObject(s)
println(regisCustomer.toString())
Since hardcoding the string with your value worked .
Try below
Gson gson = new GsonBuilder().create();
public static <T> T someMethod(String prefKey, Class<T> type) {
String prefJson = settings.getString(prefKey, "");
return gson.fromJson(prefJson , type);
}
Solution includes pieces of answers from other post.
I had to do:
prefJson= prefJson.replace("\\\"", "\"");
prefJson = prefJson.substring(prefJson.indexOf("{"), prefJson.lastIndexOf("}") + 1);
The quirky thing was the sharedpreferences had the exact string as below (including start and end quotes('"') in the value. Not sure why that was an issue, only when passed as a parameter. The quotes and the escape characters were ignored when hard coded into the string variable:
"{\"address\":{\"city\":\"City\",\"country\":\"Country\",\"customer_address_id\":0,\"customer_id\":0,\"house_number\":\"#123, Lane 1, Street 1\",\"latitude\":0.0,\"longitude\":0.0,\"postcode\":\"PA 12345\",\"street\":\"Lane 2, Street 2\"},\"ageGroup\":\"25-45\",\"dateOfBirth\":\"1537368909000\",\"email\":\"abc#abc.com\",\"firstName\":\"First Name\",\"gender\":\"\",\"id\":\"119\",\"lastName\":\"Last Name\",\"locale\":\"en_us\",\"middleName\":\"none\",\"phone\":\"0987654321\",\"uuid\":\"8c3ce2c5-600f-3c4e-bc07-727d61fae7ff\"}"
which had to be converted to:
{"address":{"city":"City","country":"Country","customer_address_id":0,"customer_id":0,"house_number":"#123, Lane 1, Street 1","latitude":0.0,"longitude":0.0,"postcode":"PA 12345","street":"Lane 2, Street 2"},"ageGroup":"25-45","dateOfBirth":"1537368909000","email":"abc#abc.com","firstName":"First Name","gender":"","id":"119","lastName":"Last Name","locale":"en_us","middleName":"none","phone":"0987654321","uuid":"8c3ce2c5-600f-3c4e-bc07-727d61fae7ff"}
Note: Moderators, please feel free to remove or merge this questiont if this sounds repetitive.Thank you everyone.
I have this JSON file into my assets. I am parsing it using Gson into the following model class:
public class SearchRequest {
private ArrayList<String> _source;
private int from;
private int size;
private Object sort;
private Object query;
public void setFrom(int from) {
this.from = from;
}
public void setSize(int size) {
this.size = size;
}
public void setArtist(String artistName) {
Gson gson = new Gson();
JsonObject object = gson.toJsonTree(query).getAsJsonObject();
JsonArray should = object.getAsJsonObject("function_score").getAsJsonObject("query")
.getAsJsonObject("bool").getAsJsonArray("should");
should.get(0).getAsJsonObject().getAsJsonObject("match").addProperty("album_artist", artistName);
should.get(1).getAsJsonObject().getAsJsonObject("nested").getAsJsonObject("query")
.getAsJsonObject("bool").getAsJsonArray("must").get(0).getAsJsonObject()
.getAsJsonObject("match").addProperty("tracks.artist", artistName);
query = gson.fromJson(object.toString(), query.getClass());
}
}
When I convert this JSON into an object of this class, the query object becomes a LinkedTreeMap. But in this conversion, the key offset which is an integer, becomes double. In my JSON (line number 50), offset is 0, but after conversion, its 0.0. Screenshot:
Why is this happening? How to fix this?
How to fix this?
It's not a subject to be fixed and nothing to be worried about.
Why is this happening?
Your JSON<->Java mapping does not provide any mappings except the top-most one. That makes Gson work like that due to lack of the target type information, and LinkedTreeMap is used internally. Literals like 0 that may look like integers are also legal floating point values from the JSON format point of view: the JSON specification declares numbers only, and does not make any corrections on "integerness". Having no enough information on deserializing the data types, Gson applies the default parsing policies, and chooses java.lang.Double as a type that can hold any other standard numeric values that can hold less significance bits (longs can fit the room of doubles easily; but not sure what Gson does for BigDecimals -- JSON specifications does not seem to make any limits). So this is just internal Gson representation and you have a few options on that:
You can declare a mapping (write or generate by a specialized tool) that would even avoid internal LinkedTreeMaps. Tedious? Maybe. But much more power in type safety, javac control, or your favorite IDE navigation and suggestions.
Gson provides a bunch of methods to convert a JSON tree value to a target type: getAsJsonObject(), getAsJsonArray(), getAsInt(), and more allowing to get the target object in the representation you want. For example,
final Object value = searchRequest.query
.get("function_score").getAsJsonObject()
.get("functions").getAsJsonArray()
.get(0).getAsJsonObject()
.get("linear").getAsJsonObject()
.get("date").getAsJsonObject()
.get("offset").getAsInt();
System.out.println(value + " " + value.getClass());
gives:
0 class java.lang.Integer
because of get("offset").getAsInt() that's internally implemented as return isNumber() ? getAsNumber().intValue() : ...; for JSON primitives.
You can apply partial mappings. For example, you can extract the date JSON tree and convert it to a special mapping having the private int offset; field declared: gson.fromJson(dateJsonObject, DateMapping.class).
Again, Gson just does not have enough type information and works really fine.
Ok so I have this piece of JSON that I want to parse with Gson. I would like the Strings to be the values and the longs to be the keys.
{"completed_questions":[["String",12345],...]}
The issue is the data type, when I try a Map<String, Long> it parses everything but gives me an error because of the duplicate String keys.
I tried to reverse it thinking Gson would know to switch them around but when I tried Map<Long, String> I got an error about not being able to parse my Strings as Longs.
To get it to work I created a swap map class that takes the Key and Value types and swaps them like so public class SwapMap<K, V> implements Map<K, V> however translating the swapped map actions like put/get/remove seem to be pretty difficult to make work.
What's the best way to parse this with Gson even though the strings aren't unique? (But the numbers are)
JSON doesn't allow identical keys on the same level in a json object. It seems like you are trying to map a json array to a java map.
Based on the following data structure, you would need a list if you want to use the default conversion provided by Gson.
{
"completed_questions": [
[
"String",
12345
],
[
"String",
12345
]
]
}
Here is a quick implementation:
private static void mapToObject() {
String json = "{\"completed_questions\":[[\"String\",12345],[\"String\",123456]]}";
Gson gson = new Gson();
CompletedQuestions questions = gson.fromJson(json, CompletedQuestions.class);
for (List<String> arr : questions.getCompleted_questions()) {
for (String val : arr) {
System.out.print(val + " ");
}
System.out.println();
}
}
public static class CompletedQuestions {
List<List<String>> completed_questions;
public List<List<String>> getCompleted_questions() {
return completed_questions;
}
}
This outputs:
String 12345
String 123456
The thing to note is that I am using a list for mapping purposes which closely resembles the data model provided.
This will require you to do the conversion to long yourself. But the way that json string looks. It seems like you would need to operate on the indices. If you have control over the json structure, I would recommending creating a better model. Other wise you can do something like list.get(0) -> your key list.get(1) -> your value which is the long on the inner list.
So what I did is just made a custom Gson Deserializer that mapped these values to a LongSparseArray<String>, which is the best way to go about it.
This is the relevant parts of the Deserializer:
for (JsonElement array : jsonObject.get("my_key").getAsJsonArray()) {
if (array.getAsJsonArray().size() == 2) {
String value = array.getAsJsonArray().get(VALUE).getAsString();
long key = array.getAsJsonArray().get(KEY).getAsLong();
progress.completedActivities.put(key, value);
}
}
Then I just added it to my Gson creator like so:
#Provides #Singleton Gson provideGson() {
return new GsonBuilder()
.registerTypeAdapter(MyClass.class, new MyClass())
.create();
}
I am using GSON Library to parse my JSON tag.
some of the tags are expected to hold string values (not arrays). the problem is, sometimes the element is empty [] and when it does that the console gives me this error
expected String but was BEGIIN ARRAY.
The following is the ideal case for my JSON
{
"internet": "600.00",
"internet_remarks": "Fibre 1gbps",
}
but sometimes it becomes
{
"internet": "600.00",
"internet_remarks": [],
}
My parsing code is as follows:
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
MainContainer mainContainer = gson.fromJson(obj, MainContainer.class);
while in my class is as follows the varailbas are defined as follows
private String internet;
private String internet_remarks;
My question is what changes should i make so that the variable accommodate the empty array []
If you don't control the source of the (poorly designed) JSON and can't fix it, then you're going to need to write a custom deserializer that constructs your MainContainer object.
See: How do I write a custom deserializer? here on SO and/or the information in the Gson User's guide
If the only time that field is an array type is when it's an empty array, the easiest approach I can think of is simply inspecting the returned JSON and if it's an array, remove it. Then deserialize to your MainContainer. Gson silently ignores any missing elements in the JSON and internet_remarks will be null in your MainContainer.
class MyDeserialier implements JsonDeserializer<MainContainer>
{
#Override
public MainContainer deserialize(JsonElement je, Type type,
JsonDeserializationContext jdc)
throws JsonParseException
{
JsonObject obj = je.getAsJsonObject();
if (obj.get("internet_remarks").isJsonArray())
{
obj.remove("internet_remarks");
}
return new Gson().fromJson(obj, MainContainer.class):
}
}
If that's not actually the case and that array might not be empty, you'll need to add the logic to deal with that and convert it to a String if that's what you really want.
In your case:
The gson parser is confused when you are asking it to parse an array into string.
So, you can change the internet_remarks into an array of string.
Its always a safe approach to use annonations in the container class.
Extras:
When ever you are working with json, these two tools will be handy.
1.OnlineJsonEditor.
2.JsonGen
I would suggest declare your second attribute as an array. i.e.
private String[] internet_remarks;
When you have a single string, still put it in the array.
eg:
{
"internet": "600.00",
"internet_remarks": ["Fibre 1gbps"]
}
When it's empty,
{
"internet": "600.00",
"internet_remarks": []
}
So your json will always be consistent.
Also, I noted that you're not really making use of the GsonBuilder. You could just create the Gson instance with a constructor than a builder.
Gson gson = new Gson();
I have a problem that I have no idea about this, can anyone help me:
Ex we have a json:
{
"status":"0",
"result": {
"object1": {
"name":"name1",
"age":"21"
},
"object2": {
"event":"new year",
"date":"date"
},
"object1_1": {
"name":"name2",
"age":"22"
},
"object2_1": {
"event":"birthday",
"date":"date"
}
}
}
you can try convert to object by using jackson json.
http://jackson.codehaus.org/
If you want to deserialize this json to an object that contains a Map (and the map contains litteral values and other maps). Assuming you have a bean similar to :
class MyBean {
int status;
Map<String, Object> result;
}
MyBean myBean = new Gson().fromJson(jsonString, MyBean.class);
It should work with no modification. Note that if the type of status is not a number I'm not sure Gson does the conversion as in the json string the value is quoted, same thing applies to your "age" property.
You can also have a look at Genson library http://code.google.com/p/genson/ it has most Gson features, other ones that no other library provide and has better performances. Have a look at the wiki http://code.google.com/p/genson/wiki/GettingStarted.
EDIT
Are the names really things like object1_1, object2_1 etc? When looking at the structure I imagine that object1 goes with object2 and so long. If you use gson you can write a custom TypeAdapter http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/TypeAdapter.html.
So you can create a root object similar to
class Response {
int status;
List<MyObject> result;
}
class MyObject {
String name;
int age;
String event;
String date;
}
In the read method of your TypeAdapter you should compose instances of MyObject based on the keys (object1 with object2, object1_1 with object2_1...) or take a similar approach.
If you want more details on how to do that you can also ask on Gson google group.