programing

com.google.gson.internal.LinkedTreeMap을 내 클래스로 캐스팅 할 수 없습니다.

shortcode 2021. 1. 19. 07:34
반응형

com.google.gson.internal.LinkedTreeMap을 내 클래스로 캐스팅 할 수 없습니다.


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);
    }
}    

내 서버 ArrayList<Product>에서 JSON String 표현을 얻습니다 . 예를 들면 :

[{"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 목록을 만든 것처럼 보이지만 어떻게 내 제품 개체로 변환합니까?


제 생각에는 유형 삭제로 인해 파서가 런타임에 실제 유형 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();

또한 LinkedTreeMaps를 캐스팅하는 것에 대해 불평하는 GSON에 문제가있었습니다.

대답 알렉시스와에 의해 제공 코멘트 오류가 발생하는 이유 Aljoscha에 의해 설명; "유형의 제네릭은 일반적으로 런타임에 지워집니다." 내 문제는 정상적으로 실행할 때 내 코드가 작동했지만 ProGuard를 사용하면 캐스팅에 중요한 코드가 제거된다는 것입니다.

Alexis의 답변을 따라 캐스트를 더 명확하게 정의하면 문제가 해결됩니다. Google에서 제공 하는 ProGuard 규칙을 추가 할 수도 있습니다 (이렇게하면 문제가 해결됨).

##---------------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 규칙이 무엇인지 항상 확인하십시오.


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)
}

또한 서명 된 빌드에 대해서만 com.google.gson.internal.LinkedTreeMap의 클래스 캐스트 예외에 직면했습니다. 나는 progurard에서 아래 줄을 추가했습니다. 그런 다음 잘 작동합니다.

-keepattributes 서명

-keepattributes 주석

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

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


대한 JSON

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

에서 목록보기 BaseAdapter 파일 우리는 다음과 같이 행 속성 값을 얻을 수 LinkedTreeMap 키 값 개체를 사용하여 데이터를 매핑해야합니다 :

...
...

    @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

소스 링크


나는 같은 문제가 있었다. 나는 당신이 List를 인수로 가질 때만 발생한다는 것을 알았습니다.

My solution is to wrap the list in another Object:

class YourObjectList {

    private List<YourObject> items;

    // constructor, getter and setter
}

With that single object, i had no more problems with class cast exception.


If you use your own ArrayList<MyObject> in gson when parsing;

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

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

{"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 
}

To add to the answers already mentioned here, if you have a generic class that handles, say, HTTP calls, it maybe useful to pass Class<T> as part of the constructor.

To give a little more detail, this happens because Java cannot infer the Class<T> during runtime with just T. It needs the actual solid class to make the determination.

So, if you have something like this, like I do:

class HttpEndpoint<T> implements IEndpoint<T>

you can allow the inheriting code to also send the class<T>, since at that point is it clear what T is.

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

inheriting class:

public class Players extends HttpEndpoint<Player> {

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

while not entirely a clean solution, it does keep the code packaged up and you don't have to Class<T> between methods.


use this when parsing

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

ReferenceURL : https://stackoverflow.com/questions/27253555/com-google-gson-internal-linkedtreemap-cannot-be-cast-to-my-class

반응형