Serialize objects with Gson into arbitrary JSON arrays without getting warnings - java

I need to serialize an object like this:
class A {
int a = 1;
String b = "hello";
boolean isDog = false;
}
into JSON array like this:
[1,"hello",false]
I know one (wrong) way to do this: create an untyped Collection out of object's fields and then Gson it:
class A {
// ...
Collection forGson() {
ArrayList col = new ArrayList();
col.add(a);
col.add(b);
col.add(c);
return col;
}
}
new Gson().toJson(new A().forGson());
But it produces a lot of warnings because of untyped collections usage. So is there any way to serialize objects into an array of arbitrary types without getting any warnings?

This is literally "You're doing it wrong". You don't have an array of random things, you have an (A) object. It has nothing in common with the JSON you want to produce.
That being said, if you really wanted to do this, you supply your own Serializer / Deserializer to Gson:
class ASerializer implements JsonSerializer<A>
{
public JsonElement serialize(A t, Type type, JsonSerializationContext jsc)
{
JsonArray ja = new JsonArray();
ja.add(new JsonPrimitive(t.a));
ja.add(new JsonPrimitive(t.b));
ja.add(new JsonPrimitive(t.isDog));
return ja;
}
}
You'd create a JsonDeserializer that did the reverse, creating a A object from the supplied JSON array.
See: https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization for more info.
Then using GsonBuilder you'd tell Gson to use them:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(A.class, new ASerializer());
builder.registerTypeAdapter(A.class, new ADeserializer());
Gson gson = builder.create();
...

Related

Gson: how to include partly serialized object?

Given the class:
class Container<T> {
T item;
String type;
Map<String,String> properties;
public void setItem(T _item) {
item = _item;
}
}
I have already the item serialized in a database as string with the name serialized. It is a Map<String,String>.
I don't know how to say Gson that this variable is already serialized.
So when I use Gson I first deserialize it, then serialize it back
Container<Map <String, String>> t = new Container<>(<other parameters>);
Map <String, String> m = gson.fromJson(serialized, new TypeToken<Map<String,String>>(){}.getType())
t.setItem(m);
gson.toJson(t, new TypeToken<Container<Map<String,String>>>() {}.getType());
This feels inefficient. How do I fix this?
I'm not sure that's possible. You're mixing object creation and serialization.
What you can do is create a new constructor with an additional String parameter and deserialize the string to get your item and set it automatically. That should be possible even with a parameterized type. That way you have 2 lines of code instead of 4.

How to create json, sorted on keys, using gson?

I need to create constant json string or a json sorted on keys. What do I mean by constant json string? Please look into following code sample, which I created.
My Code 1:
public class GsonTest
{
class DataObject {
private int data1 = 100;
private String data2 = "hello";
}
public static void main(String[] args)
{
GsonTest obj=new GsonTest();
DataObject obj2 = obj.new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
System.out.println(json);
}
}
Output 1:
{"data1":100,"data2":"hello"}
My Code 2:
public class GsonTest
{
class DataObject {
private String data2 = "hello";
private int data1 = 100;
}
public static void main(String[] args)
{
GsonTest obj=new GsonTest();
DataObject obj2 = obj.new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
System.out.println(json);
}
}
Output 2:
{"data2":"hello","data1":100}
If you see, if I switch variables (data1 & data2 in DataObject class), I get different json. My objective to get same json, even if somebody changes position of the class variables. I get it when somebody adds new variables, json would change. But json shouldn't change when variables are moved around. So, my objective is to get standard json, possibly in sorted keys order for same class. If there is nested json, then it should be sorted in the nested structure.
Expected output on run of both the codes:
{"data1":100,"data2":"hello"} //sorted on keys!! Here keys are data1 & data2
I understand, I need to change something in String json = gson.toJson(obj2); line, but what do I have to do?
Why I need them to be order?
I need to encode the json string and then pass it to another function. If I change the order of keys, even though value remain intact, the encoded value will change. I want to avoid that.
First of all, the keys of a json object are unordered by definition, see http://json.org/.
If you merely want a json string with ordered keys, you can try deserializing your json into a sorted map, and then serialize the map in order to get the sorted-by-key json string.
GsonTest obj=new GsonTest();
DataObject obj2 = new DataObject();
Gson gson = new Gson();
String json = gson.toJson(obj2);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
Like others have mentioned that by design JSON is not supposed to have sorted keys in itself. You can also come up with a recursive solution to do it. I won't say my solution is very efficient but it does the intended job. Please have a look at the following piece of code.
private static JsonObject sortAndGet(JsonObject jsonObject) {
List<String> keySet = jsonObject.keySet().stream().sorted().collect(Collectors.toList());
JsonObject temp = new JsonObject();
for (String key : keySet) {
JsonElement ele = jsonObject.get(key);
if (ele.isJsonObject()) {
ele = sortAndGet(ele.getAsJsonObject());
temp.add(key, ele);
} else if (ele.isJsonArray()) {
temp.add(key, ele.getAsJsonArray());
} else
temp.add(key, ele.getAsJsonPrimitive());
}
return temp;
}
Input:
{"c":"dhoni","a":"mahendra","b":"singh","d":{"c":"tendulkar","b":"ramesh","a":"sachin"}}
Output:
{"a":"mahendra","b":"singh","c":"dhoni","d":{"a":"sachin","b":"ramesh","c":"tendulkar"}}
Perhaps a work around is for your class wrap a TreeMap which maintains sort order of the keys. You can add getters and setters for convenience. When you gson the TreeMap, you'll get ordered keys.

Deserialize JSON object that has a map using Gson

I've searched the web for a solution, but, the answers I found only help when the JSON file being parsed is solely a map.
I was wondering if there is a way to parse an object that has an HashMap along with other data.
For example:
public class Data
{
String aName;
HashMap<String, Object> objects;
List<String> aExpressions;
}
I can parse the name and expressions but I don't know how to parse the entire JSON object as a whole.
I think it has something to do with type tokens, but that only works if the whole object is a HashMap.
The presence of HashMap in your class does not change how Gson converts it to and from json.
data d = new data();
//set some values
String json = new Gson().toJson(d);
data d2 = new Gson().fromJson( json, data.class);
That's pretty much it.
Since
JSON can represent four primitive types (strings, numbers,
booleans, and null) and two structured types (objects and arrays).
A string is a sequence of zero or more Unicode characters
[UNICODE].
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
An array is an ordered sequence of zero or more values.
Gson acts 2 ways:
you can let Gson deserialize creating a combination of maps, lists and primitives or
you can specify exactly the kind of object you want, and through reflection, it will fill the desidered fields.
You can mix these two approaches and of course you can do a lot more to solve all your parsing cases (like using custom type adapters).
I prepared you a little example using your class that shows how Gson can parse your data using a combination of maps/list/primitives or passing your class (that has a mixed approach).
package stackoverflow.questions;
import java.util.*;
import com.google.gson.Gson;
public class Q20154323 {
public static class Data {
public String aName;
public HashMap<String, Object> objects;
public List<String> aExpressions;
#Override
public String toString() {
return "Data [aName=" + aName + ", objects=" + objects + ", aExpressions=" + aExpressions + "]";
}
}
public static void main(String[] args) {
Data d = new Data();
d.aName = "Test";
d.objects = new HashMap<>();
d.aExpressions = new ArrayList<>();
d.objects.put("key1", 1L);
d.objects.put("key2", new Date());
d.aExpressions.add("Stack");
d.aExpressions.add("Overflow");
Gson g = new Gson();
String json = g.toJson(d);
System.out.println("As JSON: " +json);
Data d2 = g.fromJson(json, Data.class);
System.out.println("As \"casted\" data type: " + d2);
Object o3 = g.fromJson(json, Object.class);
System.out.println("As \"free\" object: " + o3);
}
}
and this is the execution. It shows you two way of parsing the JSON string that I created using your initial class.
As JSON: {"aName":"Test","objects":{"key2":"Nov 23, 2013 1:33:23 AM","key1":1},"aExpressions":["Stack","Overflow"]}
As "casted" data type: Data [aName=Test, objects={key2=Nov 23, 2013 1:33:23 AM, key1=1.0}, aExpressions=[Stack, Overflow]]
As "free" object: {aName=Test, objects={key2=Nov 23, 2013 1:33:23 AM, key1=1.0}, aExpressions=[Stack, Overflow]}
You can use one or other approach as your needs.
About the TypeToken, due to generics erasure, something like this
List<Data> list = new Gson().parse(aJsonString, List<Data>.class)
won't work, you have to do something like
Type listType = new TypeToken<List<Data>>() {}.getType();
List<Data> list = new Gson().parse(aJsonString, listType.class)
but this case applies, pratically, when your JSON is an array and you want to deserialize it into a list of custom classes.

Java gson generic array/list deserialization

I'm trying to deserialize a generic list using Gson.
I'm able to deserialize the following JSON:
[{"updated_at":"2012-03-09T11:13:31Z","id":1,"title":"Moda","position":0,"short_name":"Md"},
{"updated_at":"2012-03-09T11:13:40Z","id":2,"title":"Sissi","position":1,"short_name":"SI"},
{"updated_at":"2012-03-09T11:13:47Z","id":3,"title":"Levis","position":2,"short_name":"LV"},
{"updated_at":"2012-03-09T11:14:03Z","id":4,"title":"Dolce&Gabanna","position":3,"short_name":"DG"}]
with the following code:
T[] array = (T[])java.lang.reflect.Array.newInstance(p_class, 0);
gson.fromJson(content, array.getClass());
But now, I have the following JSON what I can't figure out how to deserialize with gson:
[{"brand":{"updated_at":"2012-03-09T11:13:31Z","id":1,"title":"Moda","position":0,"short_name":"Md"}},
{"brand":{"updated_at":"2012-03-09T11:13:40Z","id":2,"title":"Sissi","position":1,"short_name":"SI"}},
{"brand":{"updated_at":"2012-03-09T11:13:47Z","id":3,"title":"Levis","position":2,"short_name":"LV"}},
{"brand":{"updated_at":"2012-03-09T11:14:03Z","id":4,"title":"Dolce&Gabanna","position":3,"short_name":"DG"}}]
Thanks for your help!
You need to create a new class which has an object named brand and is a type of p_class. Then use gson on your new class as you did before and it should return you an array of your new class. for example:
class Brand{
private p_class brand;
public p_class getBrand(){
return brand;
}
}
and for gson:
List<Brand> brands = (List<Brand>) gson.fromJson(content, new TypeToken<List<Brand>>(){}.getType());
another way would be doing with ordinary json objects available in android framework:
JSONArray ar = new JSONArray(content);
for(int i=0; i<ar.length(); i++){
JSONObject obj = ar.getJSONObject(i);
//here is your desired object
p_class p = gson.fromJson(obj.getJSONObject("brand").toString(), p_class.class);
}

Gson and the argonauts - problem converting javascript array, to json string, to java pojo using gson. Trying to get my structures correct

Here is my json string, that I am acessing in java:
json =
[
{"id":"123456","Data":"skill2","dmlcrud":false},
{"id":"123456","Data":"skill3","dmlcrud":true},
{"id":"123456","Data":"skill14","dmlcrud":true},
{"id":"123321","Data":"skill1","dmlcrud":false},
{"id":"123321","Data":"skill14","dmlcrud":false}
]
I now want to put it in a collection so ideally/theoretically I would want to do:
List<Person> personList = new Gson().fromJson(json, Person.class);
and personList.size() would = 5. I would then loop through personList and preform my relevant actions.
However, my understanding is that I would need to create a container class, which itself contains the person list ? So instead of (public getters/setters removed for brevity, probably syntax errror in there aswell).
Class Person {
private integer id;
private String Data;
private Boolean dmlCrud ;
}
I would actually need something like ?
Class container{
List<Person> personList;
static class Person {
private integer id;
private String Data;
private Boolean dmlCrud ;
}
}
However I would then need to alter the javascript json to be somethign different aswell ? Which seems rather problematic as am I creating the json string from a javascript array, using JSON.stringifier.
Any help gratefully received.
EDIT
the solution I used was to add
public List<Person> personList;
to the person class
and alter the json object so that it was
{ "personList" :
[
{"id":"123456","Data":"skill2","dmlcrud":false},
{"id":"123456","Data":"skill3","dmlcrud":true},
{"id":"123456","Data":"skill14","dmlcrud":true},
{"id":"123321","Data":"skill1","dmlcrud":false},
{"id":"123321","Data":"skill14","dmlcrud":false}
]
}
the gson call can then be
Person person = new Gson().fromJson(json, Person.class);
and the data accessed in a list like so
List<Person> personList = person.getPersonList();
EDIT 2
A second, better, solution is to use this json array
[
{"id":"123456","Data":"skill2","dmlcrud":false},
{"id":"123456","Data":"skill3","dmlcrud":true},
{"id":"123456","Data":"skill14","dmlcrud":true},
{"id":"123321","Data":"skill1","dmlcrud":false},
{"id":"123321","Data":"skill14","dmlcrud":false}
]
and then use
Type listType = new TypeToken<List<SkillsGsonTO>>() {}.getType();
List<Person> personList = new Gson().fromJson(json,listType);
Person person1 = personList.get(0);
where the original class is used
Class Person {
private integer id;
private String Data;
private Boolean dmlCrud ;
}
You could use a Container class but this only makes sense if you need to ship additional properties on the person list. If this is not the case, you could convert to a java.util.List as well. I think you need to specify the "name" of the list property as a root element in your JSON string. So for instance if you're domain object is a List of Person objects, than your JSON root element is: "persons" or "personList". So you're JSON could look something like:
"persons" : {[
{"id":"123456","Data":"skill2","dmlcrud":false},
{"id":"123456","Data":"skill3","dmlcrud":true},
{"id":"123456","Data":"skill14","dmlcrud":true},
{"id":"123321","Data":"skill1","dmlcrud":false},
{"id":"123321","Data":"skill14","dmlcrud":false}
]}
I could be a little bit off with the syntax, but it should be something similar to this. So to summarize:
In your case you can leave you're Person class untouched and gson should be able to create the List persons for you from the JSON string I suggested.
From the Gson API docs:
If the object that your are deserializing is a ParameterizedType (i.e. contains at least one type parameter and may be an array) then you must use the fromJson(String, Type) method. Here is an example for deserialing a ParameterizedType:
Type listType = new TypeToken<List<String>>() {}.getType();
Gson gson = new Gson();
List<String> target2 = gson.fromJson(json, listType);
So in your case it would be:
Type listType = new TypeToken<List<Person>>() {}.getType();
List<Person> persons = new Gson().fromJson(json, listType);
where json is your json string obviously

Categories

Resources