programing

com.google.gson.internal.Linked Tree Map을 클래스에 캐스팅할 수 없습니다.

goodsources 2023. 3. 22. 21:09
반응형

com.google.gson.internal.Linked Tree Map을 클래스에 캐스팅할 수 없습니다.

JSON 문자열에서 개체를 가져오는 데 문제가 있습니다.

는 는는 class class class class class class라는 수업을 들었다.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을 객체에 캐스트할 수 없습니다.제품

내가 뭘 잘못하고 있지?Linked Tree Maps 목록을 작성한 것 같습니다만, 제품 오브젝트로 변환하려면 어떻게 해야 합니까?

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

알렉시스 C의 답변과 비슷하지만 코틀린에서요
클래스 타입을 함수로 전달하고 범용 타입이 무엇인지 명확히 합니다.
하다

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

GSON이 Linked Tree Maps 캐스팅에 대해 불평하는 것도 문제가 있었습니다.

Alexis가 제공한 답변과 Aljoscha의 코멘트는 오류가 발생하는 이유를 설명합니다. "유형의 일반은 일반적으로 런타임에 지워집니다."문제는 정상적으로 실행했을 때 코드가 작동했지만 ProGuard를 사용하면 캐스팅에 필수적인 코드가 제거된다는 것입니다.

알렉시스의 대답에 따라 캐스팅을 더 명확하게 정의하면 문제를 해결할 수 있습니다.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 규칙을 항상 확인하십시오.

의 것을 ArrayList<MyObject>시 에 입력; "gson"에 입력;

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

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

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

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 예

소스 링크

저도 같은 문제가 있었어요.List를 인수로 사용할 때만 발생한다는 것을 알았습니다.

이 솔루션은 목록을 다른 개체로 래핑하는 것입니다.

class YourObjectList {

    private List<YourObject> items;

    // constructor, getter and setter
}

그 하나의 오브젝트로 클래스 캐스팅 예외에 대한 문제는 없어졌습니다.

com.google.gson.internal을 제외한 클래스 캐스트 예외도 있었습니다.서명된 빌드 전용 Linked Tree Map입니다.progurard에 아래 행을 추가했습니다.그럼 잘 되네.

-keepattributes Signature

-keepattributes *Annotation*

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

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

구문 분석할 때 사용

  public static <T> List<T> parseGsonArray(String json, Class<T[]> model) {
    return Arrays.asList(new Gson().fromJson(json, model));
}
{"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 
}

여기에 기재되어 있는 회답에 덧붙여 예를 들면, 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>메서드 간에.

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

반응형