Sh0ck
Sh0ck

Reputation: 97

Gson being read into a LinkedTreeMap

I am having trouble with serializing JSON data.

So, I'm trying to use to generics, but somehow Gson hates me when I try to implement it. All I am trying to do is have basic abstraction with generics. Here is my code:

Main class which does the generics part:

package com.yasinyazici.riot.config.json;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;

public abstract class JsonDataParser<T> {
    protected Gson gson = new Gson();
    protected String json;

    public JsonDataParser(String json) {
        this.json = json;
    }

    protected Map<String, T> transform() {
        Type type = new TypeToken<Map<String, T>>(){}.getType();
        return gson.fromJson(json, type);
    }

    public abstract T get();
}

Here is a child of the main class:

package com.yasinyazici.riot.config.json.impl;

import com.yasinyazici.riot.config.json.JsonDataParser;
import com.yasinyazici.riot.data.summoner.SummonerProperties;

public class SummonerPropertiesParser extends JsonDataParser<SummonerProperties> {
    public SummonerPropertiesParser(String json) {
        super(json);
    }

    @Override
    public SummonerProperties get() {
        transform().entrySet().forEach(p -> System.out.println(p.getValue().getProfileIconId()));
        return null;
    }
}

As you can see, IntelliJ even recognizes #get() as "SummonerProperties".

That's my error:

Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.yasinyazici.riot.data.summoner.SummonerProperties
    at com.yasinyazici.riot.config.json.impl.SummonerPropertiesParser.lambda$get$0(SummonerPropertiesParser.java:17)
    at java.lang.Iterable.forEach(Iterable.java:75)
    at com.yasinyazici.riot.config.json.impl.SummonerPropertiesParser.get(SummonerPropertiesParser.java:17)
    at com.yasinyazici.riot.Main.main(Main.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

I am kinda confused. If I print p (the object itself), I get the json in some weird format

jungleíslife={id=8.2249757E7, name=Jungle ís Life, profileIconId=1339.0, summonerLevel=30.0, revisionDate=1.481558959E12}

Original json looks like this:

{"jungleíslife":{"id":82249757,"name":"Jungle ís Life","profileIconId":1339,"summonerLevel":30,"revisionDate":1481558959000}}

p#getValue() prints the body of the json. (in the weird format)

So it seems like something is not being parsed correctly.

Anyone? Would appreciate any kind of help/solution.

EDIT: This is not an exact duplicate, reason for that is that I'm not trying to parameterize any Type, but rather need to get the original Type (I'm not using any Class variable to access a classes fields for parsing, neither do I want to).

EDIT2: Alright, okay. Erm, I tried the example with Option 1 (see Linked question as reference), but I get the following (mind, I made the method #getRawType() in ListParameterizedType class return Map.class):

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1

Even if I keep it returning ArrayList, it throws me this:

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

EDIT: Still got problems with that..

EDIT2: Does NO ONE have a possible solution..?

Upvotes: 1

Views: 3265

Answers (1)

teppic
teppic

Reputation: 7286

You will have to construct the TypeToken in the subclass, where the concrete type information is available. Add an abstract method to your superclass:

protected abstract Type getType();

Change the parse method to use it:

protected Map<String, T> transform() {
    return gson.fromJson(json, getType());
}

Then override getType in your subclass to return the concrete type:

@Override
protected Type getType() {
    return new TypeToken<Map<String, SummonerProperties>>(){}.getType();
}

Unrelated, but the "weird format" you see when you print getValue() isn't json, it's from Object.toString().

Upvotes: 2

Related Questions