I have a collection of classes, such as this:
entityClasses = new HashMap<String, Class>();
entityClasses.put("EntityType1", EntityType1.class);
entityClasses.put("EntityType2", EntityType2.class);
I also have a JSON list of their instances as well:
String entityJSON = "[{"type":"EntityType1","name":"... attributes"},...]";
Where the type attribute will determine the class of the object that will be the target of JSON parsing. How can I parse these using gson?
I tried with the following:
String type = "EntityType1"; // I already can fetch this.
final Class entityClass = entityClasses.get(type);
new Gson().fromJson(entityJSON, new TypeToken<ArrayList<entityClass>>(){}.getType());
Which would work if entityClass was an actual class name, and not a variable that represents a class. In my case however, I get the following error:
Unknown class: 'entityClass'
So how is it possible to parse by a Class variable?
Thanks in advance!
In general, you can't do that since you should pass a class as a generic type: not List<String.class>, but List<String>.
But you can use a workaround like this: store list TypeToken's instead of list generic types:
entityClasses = new HashMap<String, TypeToken>();
entityClasses.put("EntityType1", new TypeToken<ArrayList<EntityType1>>(){});
...
String type = "EntityType1"; // I already can fetch this.
final TypeToken typeToken= entityClasses.get(type);
new Gson().fromJson(entityJSON, typeToken.getType());
Related
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.
I have a json string which looks something like this :
{"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter", "lastName":"Jones"}
]}
When I use gson parser to convert that to a Map<String,String> map. The gson converts the type to Map<String,ArrayList>. And when I try to print the class name using System.out.println(map.get("employees").getClass().toString()). But this throws an exception - java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String.
my code looks something like this
String npv = json_string_provided_above;
Map<String,String> mp = new HashMap<String, String>();
mp = new Gson().fromJson(npv,mp.getClass());
System.out.println(mp.get("employees").getClass().toString());
The json is given as input by user in string format(and it will always be a valid json string). And one thing is sure that I can't assume anything about the type of data. Because it will be provided by user.
So now actually I want that even If the user inputs something like arrays of string or arrays of objects. It should not convert the them to List,
Is there is a way I can hard code the type and keep them in Map<String,String>. so in the above example when I do map.get("employees") it should give me the [{"firstName":"John", "lastName":"Doe"},{"firstName":"Anna", "lastName":"Smith"},{"firstName":"Peter", "lastName":"Jones"}] as string not list. I am not sure how to do this.
Thank you for your help,
Jon is right, just use Map<String, Object>
String npv = json_string_provided_above;
Map<String,Object> mp = new HashMap<String, Object>();
mp = new Gson().fromJson(npv, mp.getClass());
System.out.println(mp.get("employees").toString());
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.
I've got a JSON string that I want to convert to a Map structure where Object is either a Java version of a basic type (i.e. String, Int, Double), a Map. or a List.
The sample string I'm using for my tests is:
"{\"cases\":[{\"documents\":[{\"files\":[{\"name\":\"a.pdf\"}]}]}]}"
This should read as an array of cases that each have an array of documents, that each have an array of files, that each have a name
I've tried Google's Gson, but
Gson gson = new Gson();
List<Map<String, Object>> results = gson.fromJson(dictString, List.class);
gives me:
com.google.gson.JsonParseException: The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter#561777b1 failed to deserialize json object {"cases":[{"documents":[{"files":[{"name":"a.pdf"}]}]}]} given the type interface java.util.List
and I tried Jackson, but
List<Map<String, Object>> results = (List<Map<String, Object>>) new ObjectMapper().readValue(dictString, List.class);
gave me:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.List out of START_OBJECT token
at [Source: java.io.StringReader#1c5aebd9; line: 1, column: 1]
Do you have any suggestions? Either for how to use either of the above correctly, or for another parser that gives me what I want?
Cheers
Nik
I stumbled in here with the same problem and I found a simple solution. I'm posting a more clear answer just in case it helps someone else:
String jsonString = "{ \"first-name\" : \"John\" }";
//creates, essentially a wrapper for a HashMap containing your JSON dictionary
JSONObject genericMap = new JSONObject(jsonString);
//calls to "put" and "get" are delegated to an internal hashmap
String name = (String) genericMap.get("first-name");
assertEquals("John", name); //passes
genericMap.put("last-name", "Doe"); //add any object to the hashmap
//the put methods can be used fluidly
genericMap.put("weight", 150).put("height", "5'10").put("age", "32");
//finally, you can write it back out to JSON, easily
String newJson = genericMap.toString(4); //pretty-print with 4 spaces per tab
log.debug(newJson);
this prints the following:
{
"age": "32",
"first-name": "John",
"height": "5'10",
"last-name": "Doe",
"weight": 150
}
Add this dependency to your project like this:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
Or download the JAR directly:
http://repo1.maven.org/maven2/org/json/json/20090211/json-20090211.jar
I already had this class available (it was a transient dependency of something else in our project). So be sure to check if it's already there first. You might get lucky, too.
Given your description, it sounds like your definition doesn't match up. It dounds like it should be something like: List<List<list<String>>>
It's a bit more manual but have a look here:
http://json.org/java/
This will give you a JSONObject that is much easier to use than parsing the string yourself, but you will still have to drill into it and manually build your map. Kind of a half and half solution.
The easiest thing might be just to do it yourself: use something like GSON or tapestry's JSONObject to construct a java representatin of your json, then just iterate through it and build whatever map structure you like.
Using gson library:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
First you need to create a type. Let's suppose you need a Map<String,Foo> then change:
private static final Type INPUT_MAP_TYPE = new TypeToken<Map<String, Foo>>() {}.getType();
Then, have a generic method of the type:
protected <T> T readJson(String fileName, Type type) {
InputStreamReader ir = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName));
return new Gson().fromJson(ir, type);
}
Where Type is in package java.lang.reflect;
Enjoy:
Map<String, Foo> inputParams = readJson("path/to/my.json", INPUT_MAP_TYPE);
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