Gson deserializing an Array of Objects - java

I have an object
public class Point{
int x, y;
Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString(){
String ret = "[";
ret += Integer.toString(x);
ret += ", ";
ret += Integer.toString(y);
ret += "]";
return ret;
}
}
I have been able to deserialize this object with Gson like so:
class PointDeserializer implements JsonDeserializer<Point>{
#Override
public Point deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
int[] tmp = gson.fromJson(json, int[].class);
int a = tmp[0];
int b = tmp[1];
return new Point(a,b);
}
}
Now, I use the following at last to make it work. Note that type and str are strings.
Class myClass = Class.forName(type);
Class myClassDeserializer = Class.forName(type + "Deserializer");
Gson gson = new GsonBuilder().registerTypeAdapter(myClass, myClassDeserializer.newInstance()).create();
Object ret = gson.fromJson(str, myClass);
Now here is the main problem. I want to do this for classes Point[], Point[][] and so on also.
Will I have to write a deserializer for every dimension of Point or is there a better way to do it?
Please help.

First off, you're really introducing a bit of un-needed overhead in your deserializer. There's no need to create the native Java array; you already have a JSON array:
class PointDeserializer implements JsonDeserializer<Point> {
#Override
public Point deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
return new Point(array.get(0).getAsInt(), array.get(1).getAsInt());
}
}
Once you register that with Gson via registerTypeAdapter, arrays of that type "just work":
public static void main(String[] args)
{
String json = "[ [1,2], [3,4] ]";
Gson gson = new GsonBuilder().registerTypeAdapter(Point.class,
new MyPointDeserializer()).create();
Point[] mpa = gson.fromJson(json, Point[].class);
System.out.println(mpa[1].x);
}
Output:
3
The trouble you'll run into is that you're needing to get the Class dynamically and you want an array type. You can achieve this for array types by prepending [L to the fully qualified class name:
Class myClass = Class.forName("[L" + "my.package.Point");
myClass now holds Point[].class
This kinda gets rather ugly in a hurry when you start talking about multiple dimensions. It works ... you can use additional [ to represent the dimensions ... but then you still need to get it back as an Object and cast, etc.
Honestly, the better way to do it is via a List type with a TypeToken.
public static void main(String[] args)
{
String json = "[ [[1,2]], [[3,4]] ]";
Gson gson = new GsonBuilder().registerTypeAdapter(Point.class, new MyPointDeserializer()).create();
Point[][] mpa = gson.fromJson(json, Point[][].class);
System.out.println(mpa[1][0].x);
Type listType = new TypeToken<ArrayList<ArrayList<Point>>>(){}.getType();
List<List<Point>> list = gson.fromJson(json, listType);
System.out.println(list.get(1).get(0).x);
}
Output:
33

This is how to deserialize array of objects:
Gson gson = new Gson();
Type collectionType = new TypeToken<List<YourObject>>(){}.getType();
List<YourObject> yourObjectsList= gson.fromJson(jsonString, collectionType);
Happy coding :)

GSon has its own ArrayTypeAdapter that is used internally. I suppose it will be used automatically when you try to deserialise an array, since the same is done for List and other collections.
Since its internal to GSon, you should usually not have to perform any actions to enable or use it.

Related

GSON JsonObject and LinkedTreeMap

I noticed there wasn't an easy all-inclusive (POJO-free) way to convert (in GSON) a JsonArray to a List. I found an example that uses a TypeToken and when I tried to use move this to a generic method (I'm a little hazy on my Java Generics), I found some odd behavior where JsonObject and LinkedTreeMap were confused. See example below:
public <T> List<T> jsonArrayToList(JsonArray array, Class<T> clazz) {
Gson GSON = new Gson();
Type listType = new TypeToken<List<T>>() {}.getType();
return GSON.fromJson(array,listType);
}
#Test
public void testGson() {
Gson GSON = new Gson();
String jsonString = "[ {key:\"foo\",value:\"bar\"}, {key:\"shoe\",value:\"car\"} ]";
JsonArray testArray = new JsonParser().parse(jsonString).getAsJsonArray();
Type listType = new TypeToken<List<JsonObject>>() {}.getType();
List<JsonObject> goodList = GSON.fromJson(testArray,listType);
List<JsonObject> badList = jsonArrayToList(testArray, JsonObject.class); // Actually a list of LinkedTreeMap?
System.out.println( goodList ); // [{"key":"foo","value":"bar"}, {"key":"shoe","value":"car"}]
System.out.println( badList ); // [{key=foo, value=bar}, {key=shoe, value=car}]
try {
long shoeCount = goodList.stream().filter(o -> o.get("key").getAsString().startsWith("sh")).count();
System.out.println( shoeCount ); // 1
} catch (Exception e) { System.out.println( e.getMessage() ); }
try {
long shoeCount = badList.stream().filter(o -> o.get("key").getAsString().startsWith("sh")).count();
System.out.println( shoeCount );
} catch (Exception e) {
System.out.println(e.getMessage()); // com.google.gson.internal.LinkedTreeMap cannot be cast to com.google.gson.JsonObject
}
}
I'm just curious why this occurs and/or if the generic method is incorrect.
As explained in the comments, due to Java type erasure, the TypeToken<List<T>> is actually a TypeToken<List<Object>> at runtime, regardless of what value T has. When Gson is told to deserialize as Object it uses a Java type corresponding to the JSON data, for your JSON object that is java.util.Map (with Gson's implementation LinkedTreeMap).
In general you should therefore never use any type variables when creating a TypeToken (unfortunately Gson itself does not prevent this usage).
For your specific use case you can use the method TypeToken.getParameterized. With that you can create parameterized types with type arguments specified at runtime. This comes at the cost of missing type safety at compile time, so you have to make sure to provide the correct number of arguments to getParameterized depending on the generic type (e.g. Map<K, V> requires two type arguments) and that they do not validate the type bounds of the type variable.
Here is how you could use it in your method jsonArrayToList:
public <T> List<T> jsonArrayToList(JsonArray array, Class<T> clazz) {
Gson GSON = new Gson();
Type listType = TypeToken.getParameterized​(List.class, clazz).getType();
return GSON.fromJson(array,listType);
}

Serialize list of objects, keyed by attribute

Say I have a class:
public class Person {
String name;
Int age;
}
and a list of objects of this class:
List<Person> people = ...
Normally, running this through a serializer such as Jackson or Gson would result in this:
"[{'name':'John','age':42},{'name':'Sam','age':43}]
but I am looking to serialize to a single json object where each property is a list containing the attributes, like this:
"{'name':['John','Sam'],'age':[42,43]}"
Do any of the serialization libraries support this?
I'd create a sort of "wrapper" that takes in any amount of persons and stores the fields in a way that let them be serialized that way. So in this case, you would create a series of persons, create a wrapper containing those persons and then serialize that.
public class PersonWrapper {
private int[] ages;
private String[] names;
public PersonWrapper(Person... persons) {
ages = new int[persons.length];
names = new String[persons.length];
for (int i = 0; i < persons.length; i++) {
ages[i] = persons[i].getAge();
names[i] = persons[i].getName();
}
}
}
Transform your List<Person> to new object.
class NewClass {
List<String> name;
List<Integer> ages;
}
Then pass this object through the Serializer to get:
"{'name':['John','Sam'],'age':[42,43]}"
Serialization libraries are generally not designed for stuff like that.
What you're looking for is JSON tree transformation and you can easily implement it in both Gson and Jackson.
Here is a transformation example for Gson:
final class Transformations {
private Transformations() {
}
static JsonObject transposeShallow(final Iterable<? extends JsonElement> inArray) {
final JsonObject outObject = new JsonObject();
for ( final String name : scanAllNames(inArray) ) {
final JsonArray outSubArray = new JsonArray();
for ( final JsonElement inJsonElement : inArray ) {
if ( !inJsonElement.isJsonObject() ) {
throw new IllegalArgumentException(inJsonElement + " is not a JSON object");
}
outSubArray.add(inJsonElement.getAsJsonObject().get(name));
}
outObject.add(name, outSubArray);
}
return outObject;
}
private static Iterable<String> scanAllNames(final Iterable<? extends JsonElement> jsonArray) {
final ImmutableSet.Builder<String> allNames = new ImmutableSet.Builder<>();
for ( final JsonElement jsonElement : jsonArray ) {
if ( !jsonElement.isJsonObject() ) {
throw new IllegalArgumentException(jsonElement + " is not a JSON object");
}
allNames.addAll(jsonElement.getAsJsonObject().keySet());
}
return allNames.build();
}
}
The transformations above can be incorporated into serialization process, but this would affect the structure for your objects (e.g. how to indicate transposable collections for root objects and their fields? how to introduce a new "transposing" type and incorporate it in your data model type system? how to deserialize it properly if necessary?).
Once you get a JSON tree, you can transpose it on any nesting level.
Transposing the root element is the simplest way as long as you don't need to transform nested elements.
private static final Type peopleType = new TypeToken<Collection<Person>>() {}.getType();
public static void main(final String... args) {
System.out.println(gson.toJson(people, peopleType));
System.out.println(gson.toJson(Transformations.transposeShallow(gson.toJsonTree(people, peopleType).getAsJsonArray())));
}
that gives:
[{"name":"John","age":42},{"name":"Sam","age":43}]
{"name":["John","Sam"],"age":[42,43]}
The solution above can work with almost any types in your code, but is has a small penalty for building a JSON tree.
Something like PersonWrapper as suggested by Schred might be another option but this requires wrappers for all your types and they need to be updated once your wrapped classes change.
Also, you might also be interested in libraries like Jolt that are designed for JSON transformations (not sure if it's doable in Jolt though).

Converting Arraylist<Arraylist<Integer>> to json

I am quite new to java and android so please keep that in mind.
I have an Arraylist of an Arraylist of integers. These integers are data from the GPS like Latitude, longtitude, speed at that moment and distance, added to an arraylist in OnLocationChanged and all these arraylists are then added to another arraylist.(sort of like a matrix or table)
example: [[timestamp,lat,long,distance_from_start,speed],[...],...] (all are integers)
I want to convert this Arraylist of arraylists so i can save it on the internal storage of my app for use in other activities ( like statistics of this data) and to upload it to a server. I have searched around quite a bit and found that converting an arraylist to json allows this and also makes it easier to create an SQL file of this data. The conversion of the arraylist to json seems easy enough but i can't find any examples of converting an arraylist of arraylists to json. So i dont know if the arraylists in the arraylist are converted to jsonarrays or whatever or if they will be usable and readable from the json file at all. If this is not possible, are there any other alternative ways of doing this?
Thanks a lot!
Use org.json.JsonArray library.
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
public class Test {
public static void main(String[] args) {
List<List<Integer >> list= new ArrayList<List<Integer>>();
List<Integer> list1=new ArrayList();
list1.add(10);
list1.add(20);
list.add(list1);
List<Integer> list2=new ArrayList();
list2.add(60);
list2.add(70);
list.add(list2);
JSONArray jsonArray= new JSONArray(list);
System.out.println(jsonArray);
}
}
output:
[[10,20],[60,70]]
You can use Gson from Google.
Your main functions are: toJson and fromJson.
From the javadoc:
toJson(Object src)
This method serializes the specified object into its equivalent Json representation.
fromJson(String json, Type typeOfT)
This method deserializes the specified Json into an object of the specified type.
For example:
(Serialization)
Gson gson = new Gson();
gson.toJson(1); ==> prints 1
gson.toJson("abcd"); ==> prints "abcd"
gson.toJson(new Long(10)); ==> prints 10
int[] values = { 1 };
gson.toJson(values); ==> prints [1]
(Deserialization)
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String anotherStr = gson.fromJson("[\"abc\"]", String.class);
Object Examples
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
(Serialization)
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
==> json is {"value1":1,"value2":"abc"}
Note that you can not serialize objects with circular references since that will result in infinite recursion.
(Deserialization)
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
==> obj2 is just like obj
List of Lists of Integers
List<List<Integer >> list = new ArrayList<List<Integer>>();
List<Integer> list1=new ArrayList();
list1.add(100);
list1.add(200);
list.add(list1);
List<Integer> list2=new ArrayList();
list2.add(700);
list2.add(800);
list.add(list2);
Gson gson = new Gson()
String json = gson.toJson(list);
System.out.println(json);

How to compare two JSON strings when the order of entries keep changing [duplicate]

This question already has answers here:
Testing two JSON objects for equality ignoring child order in Java [closed]
(28 answers)
Closed 7 years ago.
I have a string like - {"state":1,"cmd":1} , I need to compare this with generated output but in the generated output the order keeps changing i.e. sometimes its {"state":1,"cmd":1} other times its {"cmd":1,"state":1}.
Currently I was using equals() method to compare, What can be better way in this scenario to validate the two strings. My concern is just that both entries are present in string, order is not imp.
Jackson Json parser has a nice feature that it can parse a Json String into a Map. You can then query the entries or simply ask on equality:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
public class Test
{
public static void main(String... args)
{
String input1 = "{\"state\":1,\"cmd\":1}";
String input2 = "{\"cmd\":1,\"state\":1}";
ObjectMapper om = new ObjectMapper();
try {
Map<String, Object> m1 = (Map<String, Object>)(om.readValue(input1, Map.class));
Map<String, Object> m2 = (Map<String, Object>)(om.readValue(input2, Map.class));
System.out.println(m1);
System.out.println(m2);
System.out.println(m1.equals(m2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
The output is
{state=1, cmd=1}
{cmd=1, state=1}
true
You can also use Gson API
JsonParser parser = new JsonParser();
JsonElement o1 = parser.parse("{\"state\":1,\"cmd\":1}");
JsonElement o2 = parser.parse("{\"cmd\":1,\"state\":1}");
System.out.println(o1.equals(o2));
Try this
import java.util.*;
import com.google.gson.*;
public class CompareArray {
static Set<JsonElement> setOfElements(JsonArray arr) {
Set<JsonElement> set = new HashSet<JsonElement>();
for (JsonElement j: arr) {
set.add(j);
}
return set;
}
public static void main(String[] args) {
JsonParser parser = new JsonParser();
Set<JsonElement> arr1elems =
setOfElements(parser.parse(args[0]).getAsJsonArray());
Set<JsonElement> arr2elems =
setOfElements(parser.parse(args[1]).getAsJsonArray());
System.out.println("Arrays match? " + arr1elems.equals(arr2elems));
}
}
$ java -cp .:gson-2.2.2.jar CompareArray '[{"key1":"value1"},
{"key2":"value2"}, {"key3":"value3"}]' '[{"key3":"value3"},
{"key1":"value1"}, {"key2":"value2"}]'
Arrays match? true
I trust this is helpful to you.
You can simply use a class to define your object and JSONObject to parse your JSON object and get its properties:
Java Class:
public class MyObject{
private String state; //either int or String
private String cmd;
//getters and setters
}
Your parsing should be like this:
MyObject obj = new MyObject();
JSONParser jsonParser = new JSONParser();
// input is your string here
JSONObject jsonObject = (JSONObject) jsonParser.parse(input);
//get properties here and set them in your object
obj.setState(jsonObject.get("state"));
obj.setCmd(jsonObject.get("cmd"));
And then when you have the two objects you can easily compare their properties.

Using GSON to parse array with multiple types

I wish to use GSON to parse the following json:
[
[
"hello",
1,
[2]
],
[
"world",
3,
[2]
]
]
So, that's 1 array, containing 2 arrays. The 2 inner arrays are themselves arrays, comprised of String, int, array types.
I'm unsure how I can using Java classes to model the array which has 3 different types (String, int, array). I start with:
// String json just contains the aforementioned json string.
ArrayList<ArrayList<XXX>> data = new ArrayList<ArrayList<XXX>>();
Type arrayListType = new TypeToken<ArrayList<ArrayList<XXX>>>(){}.getType();
data = gson.fromJson(json, arrayListType);
But what should be where the 'XXX' are? I think it should be an array, but it should be an array with 3 different data types. So how can I use Java to model this?
Can any help?
Thank you.
Gson has special handling for deserializing some single-component arrays into a non-array type. For example, int data = gson.fromJson("[3]", int.class); would assign the int value 3 to data.
Of course, deserializing a single-component array into a non-array type is not required. For example, the previous example could be deserialized as int[] data = gson.fromJson("[3]", int[].class);.
Gson will also often deserialize a non-String value into a String, when asked. Applying this to the first example, String data = gson.fromJson("[3]", String.class); works just as well.
Note that it does not work to tell Gson to deserialize the first example as type Object. Object data = gson.fromJson("[3]", Object.class); results in a parse exception complaining that [3] is not a primitive.
Applied to the example in the original question above, if it's acceptable to treat all of the values as Strings, then deserialization becomes simple.
// output:
// hello 1 2
// world 3 2
public class Foo
{
static String jsonInput =
"[" +
"[\"hello\",1,[2]]," +
"[\"world\",3,[2]]" +
"]";
public static void main(String[] args)
{
Gson gson = new Gson();
String[][] data = gson.fromJson(jsonInput, String[][].class);
for (String[] data2 : data)
{
for (String data3 : data2)
{
System.out.print(data3);
System.out.print(" ");
}
System.out.println();
}
}
}
Unfortunately, with Gson I've not been able to figure out a simple deserialization approach that would allow for "better" binding to more specific and mixed types in an array, since Java doesn't provide a syntax for defining a mixed type array. For example, the preferred type of the collection in the original question might be List<List<String, int, List<int>>>, but that's not possible to define in Java. So, you gotta be content with List<List<String>> (or String[][]), or turn to an approach with more "manual" parsing.
(Yes, Java allows a type declaration of List<List<Object>>, but Object is not a specific enough type to meaningfully deserialize to. Also, as discussed, attempting to deserialize [3] to Object results in a parse exception.)
Small Update: I recently had to deserialize some sloppy JSON that included a structure not too dissimilar from that in the original question. I ended up just using a custom deserializer to create a object from the messy JSON array. Similar to the following example.
// output:
// [{MyThreeThings: first=hello, second=1, third=[2]},
// {MyThreeThings: first=world, second=3, third=[4, 5]}]
import java.lang.reflect.Type;
import java.util.Arrays;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public class FooToo
{
static String jsonInput =
"[" +
"[\"hello\",1,[2]]," +
"[\"world\",3,[4,5]]" +
"]";
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyThreeThings.class, new MyThreeThingsDeserializer());
Gson gson = gsonBuilder.create();
MyThreeThings[] things = gson.fromJson(jsonInput, MyThreeThings[].class);
System.out.println(Arrays.toString(things));
}
}
class MyThreeThings
{
String first;
int second;
int[] third;
MyThreeThings(String first, int second, int[] third)
{
this.first = first;
this.second = second;
this.third = third;
}
#Override
public String toString()
{
return String.format(
"{MyThreeThings: first=%s, second=%d, third=%s}",
first, second, Arrays.toString(third));
}
}
class MyThreeThingsDeserializer implements JsonDeserializer<MyThreeThings>
{
#Override
public MyThreeThings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
JsonArray jsonArray = json.getAsJsonArray();
String first = jsonArray.get(0).getAsString();
int second = jsonArray.get(1).getAsInt();
JsonArray jsonArray2 = jsonArray.get(2).getAsJsonArray();
int length = jsonArray2.size();
int[] third = new int[length];
for (int i = 0; i < length; i++)
{
int n = jsonArray2.get(i).getAsInt();
third[i] = n;
}
return new MyThreeThings(first, second, third);
}
}
The Gson user guide does cover handling deserialization of collections of mixed types with a similar example as this in the "Serializing and Deserializing Collection with Objects of Arbitrary Types" section.
First, I think you may be mistaken in your example above. An Array consisting of three different is a very unusual approach, to say the least. Probably your json structure is an array, containing tuples. These tuples then include an array.
Like:
[
{
"hello",
1,
[2]
},
{
"world",
3,
[2]
}
]
XXX should be an object containing:
A String
An int (or Integer)
An Array of (I guess) ints.
Then you make an array of these objects and parse the json into it.
However, your json seems really badly formed, since all members should be named, like
[
{
"str":"hello",
"intVal":1,
"intArr":[2]
},
{
"str":"world",
"intVal":3,
"intArr":[2]
}
]
If, on the other hand, the JSON really looks the way you describe it, you would have to make arrays of Object, plain and simple, and then cast them when you read them from your data structure.

Categories