com.google.gson.internal.LinkedTreeMap 无法转换为我的类[重复]

问题描述 投票:0回答:10

我在从 JSON 字符串获取对象时遇到一些问题。

我上课了

Product

public class Product {
    private String mBarcode;
    private String mName;
    private String mPrice;

    public Product(String barcode, String name, String price) {
        mBarcode = barcode;
        mName = name;
        mPrice = price;
    }

    public int getBarcode() {
        return Integer.parseInt(mBarcode);
    }

    public String getName() {
        return mName;
    }

    public double getPrice() {
        return Double.parseDouble(mPrice);
    }
}    

从我的服务器我得到一个 JSON 字符串表示形式的

ArrayList<Product>
。例如:

[{"mBarcode":"123","mName":"Apfel","mPrice":"2.7"},
{"mBarcode":"456","mName":"Pfirsich","mPrice":"1.1111"},
{"mBarcode":"89325982","mName":"Birne","mPrice":"1.5555"}] 

这个字符串是这样生成的:

public static <T> String arrayToString(ArrayList<T> list) {
    Gson g = new Gson();
    return g.toJson(list);
}

为了取回我的对象,我使用这个函数:

public static <T> ArrayList<T> stringToArray(String s) {
    Gson g = new Gson();
    Type listType = new TypeToken<ArrayList<T>>(){}.getType();
    ArrayList<T> list = g.fromJson(s, listType);
    return list;
}

但是打电话的时候

String name = Util.stringToArray(message).get(i).getName();

我收到错误 com.google.gson.internal.LinkedTreeMap 无法转换为 object.Product

我做错了什么?看起来它创建了一个 LinkedTreeMaps 列表,但我如何将它们转换为我的产品对象?

java json casting gson
10个回答
112
投票

由于类型擦除,解析器无法在运行时获取真实类型 T。一种解决方法是将类类型作为参数提供给方法。

类似这样的方法是有效的,当然还有其他可能的解决方法,但我发现这个非常清晰和简洁。

public static <T> List<T> stringToArray(String s, Class<T[]> clazz) {
    T[] arr = new Gson().fromJson(s, clazz);
    return Arrays.asList(arr); //or return Arrays.asList(new Gson().fromJson(s, clazz)); for a one-liner
}

并称其为:

String name = stringToArray(message, Product[].class).get(0).getName();

28
投票

与 Alexis C 的答案类似。但在 Kotlin 中。
只需将类类型传递给函数并阐明什么是泛型类型即可。
这是简化的示例。

inline fun <reified T> parseArray(json: String, typeToken: Type): T {
    val gson = GsonBuilder().create()
    return gson.fromJson<T>(json, typeToken)
}

这是示例调用

fun test() {
    val json: String = "......."
    val type = object : TypeToken<List<MyObject>>() {}.type
    val result: List<MyObject> = parseArray<List<MyObject>>(json = json, typeToken = type)
    println(result)
}

21
投票

我也遇到过 GSON 抱怨投射 LinkedTreeMaps 的问题。

Alexis 提供的 answer 和 Aljoscha 的 comment 解释了错误发生的原因; “类型上的泛型通常会在运行时被删除。”我的问题是,当我正常运行时,我的代码可以工作,但是使用 ProGuard 会导致代码被删除,这对于转换至关重要。

您可以按照亚历克西斯的回答,更清楚地定义演员阵容,这应该可以解决问题。您还可以添加 Google 提供的 ProGuard rules(只需这样做就可以解决我的问题)。

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

##---------------End: proguard configuration for Gson  ----------

故事的寓意:始终检查您需要什么 ProGuard 规则。


13
投票

如果解析时在gson中使用了自己的

ArrayList<MyObject>

Type typeMyType = new TypeToken<ArrayList<MyObject>>(){}.getType();

ArrayList<MyObject> myObject = gson.fromJson(jsonString, typeMyType)

3
投票

对于 JSON

{
    results: [
    {
        id: "10",
        phone: "+91783XXXX345",
        name: "Mr Example",
        email: "[email protected]"
    },
    {
        id: "11",
        phone: "+9178XXXX66",
        name: "Mr Foo",
        email: "[email protected]"
    }],
    statusCode: "1",
    count: "2"
}

listView BaseAdapter 文件中我们需要使用 LinkedTreeMap Key Value 对象来映射数据以获取行属性值,如下所示:

...
...

    @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        if(view==null)
        {
            view= LayoutInflater.from(c).inflate(R.layout.listview_manage_clients,viewGroup,false);
        }

        TextView mUserName = (TextView) view.findViewById(R.id.userName);
        TextView mUserPhone = (TextView) view.findViewById(R.id.userPhone);


        Object getrow = this.users.get(i);
        LinkedTreeMap<Object,Object> t = (LinkedTreeMap) getrow;
        String name = t.get("name").toString();

        mUserName.setText("Name is "+name);
        mUserPhone.setText("Phone is "+phone);

        return view;
    }
...
...

在 Android 示例中使用 Retrofit2 从 JSON 数据中获取 ListView

来源链接


3
投票

仅针对我的签名构建,我还面临 com.google.gson.internal.LinkedTreeMap 的类转换异常。我在 progurd 中添加了以下几行。然后就可以正常使用了。

-keepattributes Signature

-keepattributes *Annotation*

-keep class com.google.** { *; }

-keep class sun.misc.** { *; }

2
投票

我也有同样的问题。我注意到它仅在您将 List 作为参数时才会发生。

我的解决方案是将列表包装在另一个对象中:

class YourObjectList {

    private List<YourObject> items;

    // constructor, getter and setter
}

有了这个单一对象,我就不再遇到类转换异常的问题了。


1
投票

解析时使用这个

  public static <T> List<T> parseGsonArray(String json, Class<T[]> model) {
    return Arrays.asList(new Gson().fromJson(json, model));
}

0
投票
{"root": 
 [
  {"mBarcode":"123","mName":"Apfel","mPrice":"2.7"},
  {"mBarcode":"456","mName":"Pfirsich","mPrice":"1.1111"},
  {"mBarcode":"89325982","mName":"Birne","mPrice":"1.5555"}
 ]
} 


JsonObject root = g.fromJson(json, JsonObject.class);
//read root element which contains list
JsonElement e = root.get("root");
//as the element is array convert it 
JsonArray ja  = e.getAsJsonArray();

for(JsonElement j : ja){
   //here use the json to parse into your custom object 
}

0
投票

要添加到此处已经提到的答案,如果您有一个处理(例如)HTTP 调用的泛型类,则将

Class<T>
作为构造函数的一部分传递可能很有用。

更详细地说,发生这种情况是因为 Java 无法在运行时仅用

Class<T>
推断
T
。需要实际扎实的课程才能做出决定。

所以,如果你有这样的事情,就像我一样:

class HttpEndpoint<T> implements IEndpoint<T>

您可以允许继承代码也发送

class<T>
,因为此时就很清楚 T 是什么了。

public HttpEndpoint(String baseurl, String route, Class<T> cls) {
    this.baseurl = baseurl;
    this.route = route;
    this.cls = cls;
}

继承类:

public class Players extends HttpEndpoint<Player> {

    public Players() {
        super("http://127.0.0.1:8080", "/players",  Player.class);
    }
}

虽然不完全是一个干净的解决方案,但它确实将代码打包在一起,并且您不必在方法之间

Class<T>

© www.soinside.com 2019 - 2024. All rights reserved.