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
Related
I have one specific case. I need to serialize/deserialize an object to Map<String, Object>. I have a class that looks like the following:
public class Data {
public String name;
public Map<String, Object> options = new HashMap<>();
}
I can put to this options objects of any type. For instance:
public class Option {
public int id;
...
}
public class TestOpt {
public name;
...
}
and I try to serialize and deserialize it:
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
Option o = new Option();
o.id = 1;
TestOpt t = new TestOpt();
t.name = "fff";
Data data = new Data();
data.name = "data";
data.options.put("o", o);
data.options.put("t", t);
String json = mapper.writeValueAsString(data);
Data d1 = mapper.readValue(json, Data.class);
// I get error because options.get("o") contains LinkedHashMap instead of Option.class
System.out.println(((Option)d1.options.get("o")).id);
}
How can I fix this issue?
The value of the serialized json is
{"name":"data","options":{"t":{"name":"fff"},"o":{"id":1}}}
So, the problem is that the object mapper has no way to tell that the o value inside the json is an Option. The best guess is that it could be a map and thus it is deserialized as a LinkedHashMap.
If you are sure that the element o is an Option, you can convert the value using an object mapper:
Option option = mapper.convertValue(d1.options.get("o"), Option.class);
But please note, that this means that the value is again serialized and then deserialized using the right type information. You can do that, but it is not a good solution.
If it is possible, a better way would be to change your model from a generic map to a specific class that contains the type information:
class Data {
public String name;
public DataOptions options = new DataOptions();
}
class DataOptions {
public Option o;
public TestOpt t;
}
Serializing this model has the same json representation as the model using a map, and the model can be used to deserialize the json from your example.
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.
My question is very similiar to this:
Unable to parse Json array using Gson
But I cann't get the answer from it.
The answer from above link:
public static List<MapData> getData(){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<MapData>>(){}.getType();
return gson.fromJson(jsonString, type);
}
It works well, but I want to use implicit operator on generic type. See below:
public static <T> List<T> getData(Class<T> classT){
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
Type type = new TypeToken<List<T>>(){}.getType();
return gson.fromJson(jsonString, type);
}
And then I try to pass the Class argument to the method:
List<MapData> data = getData(MapData.class);
System.out.println(data.get(0).city);
Then an error was arised:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.ssc.ctq.nav.util.MapData
Can anyone tell me why I get this error? Is implicit operator is not supported in TypeToken class?
you can do like this:
Gson gson = new Gson();
String jsonString = "[{\"id\":18,\"city\":\"test\",\"street\":\"test 1\",\"zipcode\":121209,\"state\":\"IL\",\"lat\":32.158138,\"lng\":34.807838},{\"id\":19,\"city\":\"test\",\"street\":\"1\",\"zipcode\":76812,\"state\":\"IL\",\"lat\":32.161041,\"lng\":34.810410}]";
List<Map> tmpList = gson.fromJson(jsonString);
List<T> resultList = new Arraylist<T>(tmplist.size());
for(Map map:tmpList){
String tmpJson = gson.toJson(map);
resultList.add(gson.fromJson(tmpJson, classT));
}
return resultList;
I met the same problem. From the Javadoc of TypeToken:
This syntax cannot be used to create type literals that have wildcard parameters, such as Class<?> or List<? extends CharSequence>.
You must explicitly indicate the type of T in TypeToken<T>, without generics.
You can use this method in order to parse generic json string to map
public Map<String, String> getMapFromJson(String jsonString) {
Map<String, String> map = new HashMap<>();
try {
JSONObject object = new JSONObject(jsonString);
Iterator<?> iterator = object.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
if(!key.isEmpty() && !object.getString(key).isEmpty()){
map.put(key, object.getString(key));
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return map;
}
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.
This question already has answers here:
Why does Gson fromJson throw a JsonSyntaxException: Expected BEGIN_OBJECT but was BEGIN_ARRAY?
(2 answers)
Closed 4 years ago.
I have the following class :
final class CFS {
public Map<String, String> files = new HashMap<String, String>();
public List<String> directories = new ArrayList<String>();
}
And this code which should parse the json :
CFS cfs = JStorage.getGson().fromJson(JSON_STRING, CFS.class);
Where
JSON_STRING = "{\"directories\" : [\"folder1\", \"folder1/folder2\"], \"files\" : [{\"folder1\" : \"file.txt\"}, {\"folder1/folder2\" : \"file.cfg\"}]}"
JSON is:
{
"directories": ["folder1", "folder1/folder2"],
"files": [
{
"folder1": "file.txt"
},
{
"folder1/folder2": "file.cfg"
}
]
}
The error I'm getting is: Expected BEGIN_ARRAY but was STRING at line 1 column 62
But I have no idea why, the json is valid according to jsonlint.
Any idea on why I am getting this error?
If someone is getting this error in AndroidStudio :
Try two things:
Roll back to last working conditions. (Revert if you use VCS).
In build options Clean project and rebuild. (Worked for me.)
I'm fairly new to android. Excuse any mistakes if committed.
Suggestions are welcome :)
Your JSON is valid - but your mapping class isn't (parts of it don't match). In particular, the files property of your class cannot be mapped as a Map<String, String> from the given JSON. It's hard to recommend an alternate structure for storing the data without seeing a larger sample, but in general you can follow this guide when mapping between JSON structures and Java classes. This JSON:
"files": [
{
"folder1": "file.txt"
},
{
"folder1/folder2": "file.cfg"
}
]
represents an array containing objects, where each object is best represented as a map. So in essence, a list of maps. Consequently your Java object should be:
public class CFS {
private List<Map<String, String>> files = new ArrayList<Map<String, String>>(
4);
private List<String> directories = new ArrayList<String>(4);
// Constructors, setters/getters
}
Note that I've corrected your properties by making them private and adding getters/setters. With the above defined class your program should work just fine.
final Gson gson = new GsonBuilder().create();
final CFS results = gson.fromJson(json, CFS.class);
Assert.assertNotNull(results);
Assert.assertNotNull(results.getFiles());
System.out.println(results.getFiles());
Produces:
[{folder1=file.txt}, {folder1/folder2=file.cfg}]
If you find yourself needing to retain the current CFS structure though, you would need to manually parse the JSON into it.
As already Brain noted you have a array of objects so you need to convert this by your own with a custom deserializer.
Here is an example implementation:
public class q16380367 {
final class CFS {
public HashMap<String, String> files = new HashMap<String, String>();
public ArrayList<String> directories = new ArrayList<String>();
}
public static void main(String[] args) {
new q16380367();
}
public q16380367() {
String JSON_STRING = "{\"directories\" : [\"folder1\", \"folder1/folder2\"], \"files\" : [{\"folder1\" : \"file.txt\"}, {\"folder1/folder2\" : \"file.cfg\"}]}";
Gson gson = new GsonBuilder().registerTypeAdapter(
new TypeToken<HashMap<String, String>>() {
}.getType(), new CfsDeserializer()).create();
CFS foo = gson.fromJson(JSON_STRING, CFS.class);
}
private final class CfsDeserializer implements
JsonDeserializer<HashMap<String, String>> {
#Override
public HashMap<String, String> deserialize(JsonElement json,
Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
HashMap<String, String> data = new HashMap<String, String>();
JsonArray list = json.getAsJsonArray();
for (JsonElement e : list) {
Set<Entry<String, JsonElement>> entries = e.getAsJsonObject()
.entrySet();
for (Entry<String, JsonElement> entry : entries) {
data.put(entry.getKey(), entry.getValue().getAsString());
}
}
return data;
}
}
}