I have two HashMaps to be serialised to JSON using Google Gson library:
final Map<String, String> map1 = new HashMap<String, String>() {
{
put("abc", "def");
}
};
final Map<String, String> map2 = new HashMap<String, String>();
map2.put("abc", "def");
final Gson gson = new Gson();
final String s1 = gson.toJson(map1); // "null"
final String s2 = gson.toJson(map2); // {"abc":"def"}
Why is the second HashMap correctly serialized but not the first HashMap?
Gson uses reflection and doesn't care whether or not a class implements Serializable. Gson does, however, need a no-arg constructor (see the design doc):
Gson needs to create a dummy class instance before it can deserialize
Json data into its fields ... we create class instances by invoking the
parameterless constructor ...
When you call toJson(Object obj) Gson will use obj.getClass() to figure out how to construct a "dummy instance" of obj which in your case is an anonymous inner class. Inner classes require a reference to their containing class in order to be constructed. The reference to the outer class is not available at the time of serialization which is why your result is null. You can get around this issue by providing Gson with a little more information about how to construct your object:
final Map<String, String> map1 = new HashMap<String, String>() {
{
put("abc", "def");
}
};
Gson gson = new Gson();
String json = gson.toJson(map1, new TypeToken<Map<String, String>>() {}.getType()); // {"abc":"def"}
Edit: Note that this only works because your anonymous class can be cast to Map<String, String>. If you had a more complex inner class such as:
final Map<String, String> map1 = new HashMap<String, String>() {
private String additionalData = "Foo";
{
put("abc", "def");
}
};
additionalData would not be in the output.
Related
I have a class
public class Car {
public String color = null;
public String brand = null;
public HashMap<String,String> attributes;
public Car() {
this.attributes = new HashMap<>();
}
public static void main( String[] args ) {
Car car = new Car();
car.color = "Red";
car.brand = "Hyundai";
car.attributes.put("key1", "value1");
car.attributes.put("key2", "value1");
Gson gson = new Gson();
String json = gson.toJson(car);
System.out.println(json);
}
}
This currently serializes my car object into
{"color":"Red","brand":"Hyundai","attributes":{"key1":"value1","key2":"value1"}}
But I would like to unpack and serialize the attributes Dictionary from Car class as individual properties rather dictionary.
Ideally, i would like my json to be,
{"color":"Red","brand":"Hyundai","key1":"value1","key2":"value1"}
How do I achieve the same in GSON?
As explained in this Thread, there is no easy or straight forward way to achieve this.
If there is a scope of using Jackson, it can be achieved using #JsonUnwrapped but it doesn't work with Maps as explained here
but there is work around.
#JsonIgnore
public HashMap<String,String> attributes;
#JsonAnyGetter
public HashMap<String, String> getAttributes() {
return attributes;
}
And this will help create the required JSON.
(new ObjectMapper()).writeValueAsString(car)
Note. This is an alternate approach.
Use #JsonAnyGetteron the getter of your attributes
here is my pojo
public class Data{
List<Object> objects;
String owneruid;
}
if the out put is pure json like this
{"object":[{"p1":100,"p2":"name","p3":"sfa0","p4":300}],"owneruid":"owneruid"}
then iam able to convert with no worries but
here is my output
{
"object":"[{\"p1\":32,\"p3\":470,\"p3\":\"213\",\"p4\":\"name\"}]",
"owneruid":"6697729776330393738"
}
im converting a json string to string because to store in my db as it does not accept json so when i query returns like above so every time i need to fetch the value and convert it to json object and put it in list and display. can you suggest me a better approach.
And when i try to convert a list of custom classes to json using GSON
ArrayList<Object> list=new ArrayList<>();
Object object=new Object();
object.setP1(3);
object.setP2(4);
list.add(object);
Gson gson=new Gson();
String json = gson.toJson(list);
Required:
{"object":[{"p1":100,"p2":"name","p2":"sfa0","p4":300}],"owneruid":"owneruid"}
buts it ends like this
{"object":"[{\"p1\":313,\"p2\":470,\"p3\":\"1521739327417\",\"p4\":\"name\"}]","owneruid":"6697729776330393738"}
You have to use any json frameworks. E.g. Jackson or Gson. As alternative you could do smth. like this. Just evaluate JavaScript.
public static void main(String... args) throws ScriptException {
ScriptEngine js = new ScriptEngineManager().getEngineByName("javascript");
Object obj = js.eval("[{\"width\":313,\"height\":470,\"mediauid\":\"1521739327417\",\"mediatype\":\"image\"}]");
// res is either List<Object> or Map<String, Object>
Object res = convertIntoJavaObject(obj);
}
private static Object convertIntoJavaObject(Object obj) {
if (!(obj instanceof ScriptObjectMirror))
return obj;
ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
if (mirror.isArray())
return mirror.entrySet().stream()
.map(entry -> convertIntoJavaObject(entry.getValue()))
.collect(Collectors.toList());
Map<String, Object> map = new HashMap<>();
mirror.entrySet().forEach((key, value) -> map.put(key, convertIntoJavaObject(value)));
return map;
}
You can use the below code snippet as it seems fit for your case.
ObjectMapper can be found with Jackson framework. inputJson is the JSON string you have mentioned.
ObjectMapper mapper = new ObjectMapper();
Object mediaMetaDataObj = mapper.readValue( inputJson, Object.class );
Hope this helps.
Cannot figure out where the String casting is coming from that is causing this ClassCastException. I've cleared out the map so that it only holds a single entry (115,1563) and I ensured both parameters were integers.
First I read from a file and populate the scoreMap.
private void populateScoreMap(String toConvert)
{
Gson gson = new GsonBuilder().create();
ScoreRecord.scoreMap = (TreeMap<Integer,Integer>) gson.fromJson(toConvert, ScoreRecord.scoreMap.getClass());
}
ScoreRecord class
public class ScoreRecord
{
public static SortedMap<Integer,Integer> scoreMap = new TreeMap<Integer, Integer>();
}
Then I try to add an entry in the ScoreGraph class
private void addTodaysScore() {
Integer todaysScore = getIntent().getIntExtra("score",0);
Calendar calendar = Calendar.getInstance();
Integer dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
ScoreRecord.scoreMap.put(dayOfYear,todaysScore);
}
Exception
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at java.lang.Integer.compareTo(Integer.java:1044)
at java.util.TreeMap.put(TreeMap.java:593)
at com.xxx.xxx.xxx.xxx.ScoreGraph.addTodaysScore(ScoreGraph.java:63)
The problem is that the result of ScoreRecord.scoreMap.getClass() is a Java class which does not contain the information relating to generics. In your specific case, it is just SortedMap which is equivalent to SortedMap<Object, Object> rather than SortedMap<Integer, Integer>.
What you need to do is to create what Gson calls a »type token«. This will give Gson the required hints to be able to parse your collection successfully:
private void populateScoreMap(String toConvert)
{
Gson gson = new GsonBuilder().create();
Type collectionType = new TypeToken<SortedMap<Integer, Integer>>(){}.getType();
ScoreRecord.scoreMap = gson.fromJson(toConvert, collectionType);
}
This is also explained in Gson's documentation
I have a Map<String, Object> which contains a deserialized form of JSON. I would like to deserialize this into the fields of a POJO.
I can perform this using Gson by serializing the Map into a JSON string and then deserializing the JSON string into the POJO, but this is inefficient (see example below). How can I perform this without the middle step?
The solution should preferably use either Gson or Jackson, as they're already in use by the project.
Example code:
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
public class Test {
public static void main(String[] args) {
Map<String, Object> innermap = new HashMap<String, Object>();
innermap.put("number", 234);
innermap.put("string", "bar");
Map<String, Object> map = new HashMap<String, Object>();
map.put("number", 123);
map.put("string", "foo");
map.put("pojo2", innermap);
Gson gson = new Gson();
// How to perform this without JSON serialization?
String json = gson.toJson(map);
MyPojo pojo = gson.fromJson(json, MyPojo.class);
System.out.println(pojo);
}
}
class MyPojo {
private int number;
private String string;
private MyPojo pojo2;
#Override
public String toString() {
return "MyPojo[number=" + number + ", string=" + string + ", pojo2=" + pojo2 + "]";
}
}
Using Gson, you can turn your Map into a JsonElement, which you can then parse in exactly the same way using fromJson:
JsonElement jsonElement = gson.toJsonTree(map);
MyPojo pojo = gson.fromJson(jsonElement, MyPojo.class);
This is similar to the toJson method that you are already using, except that it avoids writing the JSON String representation and then parsing that JSON String back into a JsonElement before converting it to your class.
In Jackson you can use convertValue method. See below example:
mapper.convertValue(map, MyPojo.class);
You can use jackson library for convert map object to direct POJO.
Map<String, String> map = new HashMap<>();
map.put("id", "5");
map.put("name", "Bob");
map.put("age", "23");
map.put("savings", "2500.39");
ObjectMapper mapper = new ObjectMapper();
MyPojo pojo = mapper.convertValue(map, MyPojo.class);
You can directly construct MyPojo by giving it the map.
Something like
MyPojo pojo = new MyPojo(map);
And declaring an according constructor :
public MyPojo(Map<String, Object> map){
this.number=map.get("number");
this.string=map.get("string");
this.pojo2=new MyPojo(); // I don't know if this is possible
this.pojo2.string=map.get("pojo2").get("string");
this.pojo2.number=map.get("pojo2").get("number");
}
I have the following class:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
import java.util.HashMap;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Theme implements Serializable {
#JsonProperty
private String themeName;
#JsonProperty
private boolean customized;
#JsonProperty
private HashMap<String, String> descriptor;
//...getters and setters for the above properties
}
When I execute the following code:
HashMap<String, Theme> test = new HashMap<String, Theme>();
Theme t1 = new Theme();
t1.setCustomized(false);
t1.setThemeName("theme1");
test.put("theme1", t1);
Theme t2 = new Theme();
t2.setCustomized(true);
t2.setThemeName("theme2");
t2.setDescriptor(new HashMap<String, String>());
t2.getDescriptor().put("foo", "one");
t2.getDescriptor().put("bar", "two");
test.put("theme2", t2);
String json = "";
ObjectMapper mapper = objectMapperFactory.createObjectMapper();
try {
json = mapper.writeValueAsString(test);
} catch (IOException e) {
e.printStackTrace();
}
The json string produced looks like this:
{
"theme2": {
"themeName": "theme2",
"customized": true,
"descriptor": {
"foo": "one",
"bar": "two"
}
},
"theme1": {
"themeName": "theme1",
"customized": false,
"descriptor": null
}
}
My problem is getting the above json string to de-serizlize back into a
HashMap<String, Theme>
object.
My de-serialization code looks like this:
HashMap<String, Themes> themes =
objectMapperFactory.createObjectMapper().readValue(json, HashMap.class);
Which de-serializes into a HashMap with the correct keys, but does not create Theme objects for the values. I don't know what to specify instead of "HashMap.class" in the readValue() method.
Any help would be appreciated.
You should create specific Map type and provide it into deserialization process:
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Theme.class);
HashMap<String, Theme> map = mapper.readValue(json, mapType);
You can use TypeReference class which does the type casting for map with user defined types. More documentation at https://github.com/FasterXML/jackson-databind/
ObjectMapper mapper = new ObjectMapper();
Map<String,Theme> result =
mapper.readValue(src, new TypeReference<Map<String,Theme>>() {});
You can make a POJO that extends a Map.
This is important for dealing with nested maps of objects.
{
key1: { nestedKey1: { value: 'You did it!' } }
}
This can be deserialized via:
class Parent extends HashMap<String, Child> {}
class Child extends HashMap<String, MyCoolPojo> {}
class MyCoolPojo { public String value; }
Parent parent = new ObjectMapper().readValue(json, Parent.class);
parent.get("key1").get("nestedKey1").value; // "You did it!"