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.
Related
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());
the problem is to fill Java object fields both of java standart types(Long, Boolean, etc.) and collections of those from a Map<String, List<String>>.
Inspecting target object's fields via reflecton one can see which field is a Collection and which is not. In second case if Map element has a single value one can use something like BeanUtils to set such fields:
BeanUtils.setProperty(bean, name, stringList.get(0));
But in first case I don't have idea how to determine a type of collection(except of sequentially checking if it is List, Set, Map) and type of it's elements. Simple
BeanUtils.setProperty(bean, name, stringList);
will be valid only if target field type is a List<String>.
But the Jackson library succesfully solves the same problem. For example, consider class:
public static class TestObject {
private int integerField;
private String stringField;
private List<String> stringList;
private List<Integer> intList;
...getters...setters...
}
Now it can be filled with content using Jackson library:
ObjectMapper mapper = new ObjectMapper();
TestObject obj = mapper.readValue("{" +
"\"stringField\": \"stringValue\", " +
"\"integerField\": 42, " +
"\"stringList\":[\"1\", \"2\", \"3\"]," +
"\"intList\":[3, 2, 1]" +
"}", TestObject.class);
Works perfect. So my question: is there way to use a library such Jackson for solving my problem?
Thanks.
I need to create a JSONObject from a HashMap of a custom class's toString and a float value. I tried the code below hoping that it would just work:
public class MyClass {
...
public String toString() {
return "a nice string"
}
}
HashMap<MyClass,Float> map = new HashMap<MyClass,Float>();
map.put(...);
JSONObject json = new JSONObject(map);
But I get:
java.lang.ClassCastException: MyClass cannot be cast to java.lang.String
What's the best way to create this JSONObject? Thanks.
You need to change this:
HashMap<MyClass,Float> map = new HashMap<MyClass,Float>();
with
HashMap<String,Float> map = new HashMap<String,Float>();
as you said "HashMap of a custom class's toString and a float value"
You haven't mentioned how are you putting the values into the hashmap.
But if you using toString method of your custom class, then you should put it like :
MyClass m = new MyClass();
map.put(m.toString,123.45f);
Seems like you're using the org.json library. If you take a look at the code of the JSONObject class, apparently they're not using generics.
public JSONObject(Map map) {
this.map = new HashMap();
if (map != null) {
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry e = (Map.Entry)i.next();
Object value = e.getValue();
if (value != null) {
this.map.put(e.getKey(), wrap(value));
}
}
}
}
This map seems to handle entries with a String key and an Object value by the look of the keyPool map they use to manage unique String keys. In the comments, its also stated that:
This is used by JSONObject.put(string, object).
So it would be correct to assume the keys of the JSON objects are Strings.
Your MyClass type can't be upcasted to String directly (String is not a superclass of MyClass), that's why the constructor is actually complaining about the map, because it needs a map of the form HashMap<String,Object> (Note that there's no problem with Float and Object).
To fix the issue, you have to define a HashMap<String,Float> where you should store a String representation of your MyClass object either by using toString.
If you can't use a String you can consider using an intermediate structure that maps a code represented with a String to a certain MyClass object, so you can retain your MyClass class.
Both Gamb's and Abu's answers are correct and helped me to get to my final result.
I solved my problem like this:
HashMap<MyClass,Float> obj = functionThatReturnsThisStructure();
JSONObject jsonObj = new JSONObject();
for (Entry<MyClass,Float> entry: obj.entrySet()) {
jsonObj.put(entry.getKey().toString(), entry.getValue());
}
I am trying to find a way to store a new instance of a class as the value in a Java hashmap. The idea was given to me by a Java instructor in order to create a data storage structure that could be used to for a program I am working on.
The idea he recommended to me was to use a hashmap that stored the name of a computer as the key and the value would be a new instance of the class InfoStor.class. InfoStor contains methods such as getName(), setName(), getMemory()...
I have the class and the method pretty much setup for a basic test to see if it would work or not. The problem I am running into is I cannot figure out how to use the methods inside of InfoStor once I have created a new entry in the hashmap.
This is the code I have so far;
VMware.class
import java.util.HashMap;
public class VMware {
public static void main(String[] args) {
HashMap <String, Object> mapper = new HashMap();
mapper.put("NS01", new InfoStor("NS01"));
//mapper.get("NS01").
}
}
InfoStor.class
public class InfoStor {
private String vmName;
private String platform;
private Integer memory;
public InfoStor (String name) {
vmName = name;
}
String getName(){
return vmName;
}
void setPlatform(String p){
platform = p;
}
String getPlatform(){
return platform;
}
void setMemory(Integer m){
memory = m;
}
Integer getMemory(){
return memory;
}
}
What I am trying to accomplish is something like this (basic idea).
Object var = mapper.get("NS01");
System.out.println(var.getMemory());
Am I going about this the wrong way? Any help is appreciated thanks.
The problem is that your code only specifies that the values in the map are Object. You know more than that, so tell the compiler that information:
HashMap<String, InfoStor> mapper = new HashMap<String, InfoStor>();
mapper.put("NS01", new InfoStor("NS01"));
...
InfoStor value = mapper.get("NS01");
Integer memory = value.getMemory();
Note that it's generally though not always better to use interfaces for the variable types - and you can use the diamond operator for the constructor call, letting the compiler use type inference to fill in the type arguments:
Map<String, InfoStor> mapper = new HashMap<>();
mapper.put("NS01", new InfoStor("NS01"));
...
InfoStor value = mapper.get("NS01");
Integer memory = value.getMemory();
If you declare your hashmap like so:
HashMap<String, InfoStor> mapper = new HashMap<String, InfoStor>();
Then when you get an object out of the mapper, it will be an instance of InfoStor (you won't need to cast it or worry about a class cast exception because it's not the rist class.)
So:
InfoStor myStor = mapper.get("somekey");
myStor.getMemory(); // this will work
Otherwise, if you stick with the HashMap<String, Object> you used in your original code, you'll need to cast it before you call the method:
Object obj = mapper.get("somekey");
((InfoStor)obj).getMemory(); // cast is required
obj.getMemory(); // this will not compile
You should read up on Java generics.
Make use of the generics added to java. They help with both compile-time type-checking and they make the casts unnecessary.
HashMap <String, Object> mapper = new HashMap();
//you will be able to retrieve an object and then cast it to your InfoStore
InforStore isN01 = (InfoStore)mapper.get("N01");
//this will unfortunately be accepted, even thought it's a bug
mapper.put("N02", new Integer(0));
________________________
HashMap <String, InfoStore> mapper = new HashMap();
//you will be able to retrieve an object and then cast it to your InfoStore
InforStore isN01 = mapper.get("N01"); //no cast
Youre on the right track...
Initialise the map as:
HashMap <String, InfoStor> mapper = new HashMap<String, InfoStor>();
Then after adding objects to the map retrieve them with:
InfoStor var = mapper.get("NS01");
System.out.println(var.getMemory());
you can cook something by using array...for example if you can store objects in arrays then use that idea to achieve it in hash map...i dont knw how you design but i once got stuck in that and made through like this
example...
class princess{
int age;
public princess(int age){
this.age=age;
}
public int getAge(){
return this.age;
}
}
public class hashmaptest {
public static void main(String[] args) {
princess[] p=new princess[10];
HashMap scores = new HashMap();
scores.put("a",new princess(6));
scores.put("b",new princess(7));
p[0]=(princess)scores.get("a");
System.out.println(p[0].getAge());
p[0]=null;
p[0]=(princess)scores.get("b");
System.out.println(p[0].getAge());
}
}
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