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 нельзя передать объекту. Продукт
Что я делаю неправильно? Похоже, что он создал список LinkedTreeMaps, но как я могу преобразовать их в свой объект продукта?
Ответы
Ответ 1
По-моему, из-за стирания типа анализатор не может получить реальный тип 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();
Ответ 2
У меня также были проблемы с GSON, жалующимися на литье LinkedTreeMaps.
Ответ , предоставленный Алексисом, и комментарий от Aljoscha объясняет, почему возникает ошибка; "Дженерики типа обычно удаляются во время выполнения". Моя проблема заключалась в том, что мой код работал, когда я запускал его в обычном режиме, но с помощью кода ProGuard был отключен код, который был жизненно важен для кастинга.
Вы можете следить за ответами Алексиса и более четко определять актерский состав, и это должно устранить проблемы. Вы также можете добавить ProGuard правила, предоставленные Google (просто это очистило проблему для меня).
##---------------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 вам нужны.
Ответ 3
Похоже на ответы Алексея С. но в котлине.
Просто передайте тип класса в функцию и выясните, что такое универсальный тип.
Вот упрощенный пример.
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)
}
Ответ 4
Я также столкнулся с классом исключение com.google.gson.internal.LinkedTreeMap только для моей подписанной сборки. Я добавил эти строки в прогурар. Тогда он отлично работает.
-keepattributes Подпись
-keepattributes Annotation
-keep class com.google. ** {*; }
-keep class sun.misc. ** {*; }
Ответ 5
Для 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 нам нужно отобразить данные, используя объект KeyedTreeMap 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;
}
...
...
ListView из данных JSON с использованием Retrofit2 в примере Android
Ссылка на источник
Ответ 6
Если вы используете свой собственный ArrayList<MyObject>
в gson при разборе;
Type typeMyType = new TypeToken<ArrayList<MyObject>>(){}.getType();
ArrayList<MyObject> myObject = gson.fromJson(jsonString, typeMyType)
Ответ 7
{"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
}
Ответ 8
Чтобы добавить к уже упомянутым ответам, если у вас есть общий класс, который обрабатывает, скажем, 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>
между методами.
Ответ 9
используйте это при разборе
public static <T> List<T> parseGsonArray(String json, Class<T[]> model) {
return Arrays.asList(new Gson().fromJson(json, model));
}
Ответ 10
У меня такая же проблема. Я заметил, что это происходит только тогда, когда у вас есть список в качестве аргумента.
Мое решение состоит в том, чтобы обернуть список в другой объект:
class YourObjectList {
private List<YourObject> items;
// constructor, getter and setter
}
С этим единственным объектом у меня больше не было проблем с исключением классов.