Google Gson - liste <class> nesnesinin serileştirilmesi? (genel tür)


441

Bir liste nesnesini Google Gson aracılığıyla aktarmak istiyorum, ancak genel türlerin serileştirmesini nasıl yapacağımı bilmiyorum.

Ben baktıktan sonra çalıştı Ne bu (BalusC cevabı):

MyClass mc = new Gson().fromJson(result, new List<MyClass>(){}.getClass());

ama sonra "Yeni Liste () {} miras alınan soyut yöntemi uygulamak gerekir ..." diyen bir hata alıyorum ve hızlı bir düzeltme kullanırsanız 20'den fazla yöntem saplamaları bir canavar olsun.

Daha kolay bir çözüm olduğundan eminim ama bulamıyorum!

Düzenle:

Şimdi sahibim

Type listType = new TypeToken<List<MyClass>>()
                {
                }.getType();

MyClass mc = new Gson().fromJson(result, listType);

Ancak, "fromJson" satırında aşağıdaki özel durumu olsun:

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

Ben bunu yakalamak JsonParseExceptions ve "sonuç" boş değil.

ListType'ı hata ayıklayıcı ile kontrol ettim ve aşağıdakileri aldım:

  • liste Türü
    • args = ListOfTypes
      • list = boş
      • resolvedTypes = Tür [1]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = boş
    • rawType = Sınıf (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

bu nedenle "getClass" çağrısının düzgün çalışmadığı anlaşılıyor. Herhangi bir öneri...?

Edit2: Gson Kullanım Kılavuzu'nda kontrol ettim . Json'a genel bir tür ayrıştırılırken olması gereken bir Çalışma Zamanı Özel Durumundan bahseder. Örnekte olduğu gibi "yanlış" yaptım (yukarıda gösterilmiyor), ancak bu istisnayı anlamadım. Bu yüzden serileştirmeyi önerilen kullanıcı kılavuzunda olduğu gibi değiştirdim. Yine de yardımcı olmadı.

Edit3: Çözüldü, aşağıdaki cevabımı gör.


1
İşaret ettiğiniz cevap, kullanır TokenType. Bu şekilde denedin mi?
Nishant

bir cevapla aynı ipucunu aldım. bir dahaki sefere örneğe daha yakından bakacağım. ;)
denizanası

Listenin tür belirtecinde uygulanmasını deneyebilir misiniz? Ham türünüz dizi listesi olduğundan dizi listesini denemelisiniz.
uncaught_exceptions

Yanıtlar:


954

Genel koleksiyonun serisini kaldırma yöntemi:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Yorumlardaki birkaç kişi bundan bahsettiğinden, TypeTokensınıfın nasıl kullanıldığına dair bir açıklama . Yapı , bir çalışma zamanı nesnesine new TypeToken<...>() {}.getType()( <ve arasında >) bir derleme zamanı türü yakalar java.lang.reflect.Type. ClassYalnızca ham (silinmiş) bir türü temsil edebilen bir nesnenin aksine, nesne, Typegenel bir türün parametreli bir örneği de dahil olmak üzere Java dilinde herhangi bir türü temsil edebilir.

TypeTokenEğer doğrudan inşa etmek gerekiyordu çünkü sınıf kendisi bir public kurucu yoktur. Bunun yerine, her zaman anonim bir alt sınıf inşa edersiniz (bu nedenle, {}bu ifadenin gerekli bir parçasıdır).

Tür silme nedeniyle, TypeTokensınıf yalnızca derleme zamanında tam olarak bilinen türleri yakalayabilir. (Yani, new TypeToken<List<T>>() {}.getType()bir tür parametresi için yapamazsınız T.)

Daha fazla bilgi için TypeTokensınıfın belgelerine bakın .


31
GSON'un yeni sürümlerinde TypeToken yapıcısı herkese açık değildir, bu nedenle burada yapıcı görünmez hatası alırsınız. Bu durumda ne yapman gerekiyor?
Pablo

8
GSON'un (2.2.4) gerçek sürümünü kullanarak tekrar çalışır. Yapıcıya buradan erişebilirsiniz.

json nesnem bir nesne ile başlar, sonra istediğim nesnenin bir dizisini içerir, { "myObjectArray":[ {....} , {....} , {....} ] }model dosyasını yaptım, {....}bu genel koleksiyon kodunu kök öğemin yeni bir iç içe nesne dosyası yapmadan bir dizi olduğunu varsaymamak için nasıl alabilirim
CQM

7
Aşağıdaki İthalat gerekli --- import java.lang.reflect.Type; import com.google.gson.reflect.TypeToken
Umair Saleem

4
Sınıfınız kodda sabitlenmişse bu iyidir. Sınıf çalışma zamanında gelirse ne olur?
jasxir

273

Başka bir yol, bir diziyi tür olarak kullanmaktır, örneğin:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

Bu şekilde, Type nesnesiyle tüm zorluklardan kaçınırsınız ve gerçekten bir listeye ihtiyacınız varsa diziyi her zaman bir listeye dönüştürebilirsiniz:

List<MyClass> mcList = Arrays.asList(mcArray);

IMHO bu çok daha okunabilir.

Ve bunu gerçek bir liste haline getirmek için (değiştirilebilir, sınırlamalarına bakın Arrays.asList()) sonra aşağıdakileri yapın:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));

4
bu harika! Yansıması ile nasıl kullanabilirim? Ben MyClassdeğeri bilmiyorum ve dinamik olarak tanımlanacaktır!
Amin Sh

1
not: Bununla, mcList'in tam teşekküllü bir liste olmadığına dikkat edin. pek çok şey çalışmaz.
njzk2

4
Jeneriklerle nasıl kullanılır? T[] yourClassList = gson.fromJson(message, T[].class);// tip değişkeni arasından seçim
yapılamıyor

2
@MateusViccari bu yorum sırasında mcList, bu cevapta sadece çağrı çağrısı oldu Arrays.asList. Bu yöntem, isteğe bağlı yöntemlerin hepsinin olmasa da çoğunun uygulanmadığı ve istisnalar attığı bir liste döndürür. Örneğin, bu listeye herhangi bir öğe ekleyemezsiniz. Daha sonraki düzenlemenin önerdiği gibi Arrays.asList, sınırlamaları vardır ve bunu bir gerçek haline ArrayListgetirmek, birçok durumda daha yararlı bir liste almanızı sağlar.
njzk2

2
İsteğe bağlı bir öğe türü için çalışma zamanında bir dizi türü oluşturmanız gerekirse, David Wood'un yanıtındaArray.newInstance(clazz, 0).getClass() açıklandığı gibi kullanabilirsiniz .
Daniel Pryden

31

Bu gönderiye bakın. GSON için Bağımsız Değişken Olarak Java Type Generic

Bunun için daha iyi bir çözümüm var. Sargıcının tam liste türünü saklayabilmesi için liste için sarıcı sınıfı.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

Ve sonra kod basit olabilir:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}

Nedir mEntity.rulePattern?
Al Lelopath

Bu sadece test için örnek bir nesne. Bunu önemsemenize gerek yok. ToList yöntemini kullanın ve her şey yolunda.
Mutlu

@Happier Bu Gson 2.8.2'yi uygulamaya çalışıyorum ve işe yaramıyor gibi görünüyor. Herhangi bir şans stackoverflow.com/questions/50743932/… bir göz atabilir ve neyi kaçırdığımı bana bildirebilirsin
Praveen

1
@Praveen 2.8.2'de bu şekilde denedim, orijinal olarak çalışıyor.
Daha mutlu

31

Beri Gson 2.8, gibi util fonksiyonu oluşturabilirsiniz

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

Kullanım örneği

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);

2
TypeToken#getParameterizedanonim bir alt sınıf ile kesmek daha iyi bir yol görünüyor
Nikolay Kulachenko

bu kabul edilen cevap olmalıdır; en azından yeni sürümler için
ccpizza

2
Bu mükemmel bir cevap. Sorunumu çözer.
donmj

Yönteminizi "olduğu gibi" kopyaladım ve çalışmıyor: derleyici "TypeToken türü için getParameterized (Sınıf <List>, Sınıf <T>) yöntemi tanımsız" diyor. Benim iki GSON sürümü (2.8.0) ve dokümantasyon kontrol ve her şey bu tarafta gayet ... Ben cezası işleri @Happier çözüm kullanarak sona erdi
leguminator

@ leguminator TypeToken'i doğru aktardınız mı? ve java veya kotlin kullanıyorsunuz. Tekrar test etmeye çalışacağım
Phan Van Linh

26

Wep, aynı sonucu elde etmenin başka bir yolu. Okunabilirliği için kullanıyoruz.

Bu okunması zor cümleyi yerine:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

Nesnenizin bir listesini genişleten boş bir sınıf oluşturun:

public class YourClassList extends ArrayList<YourClass> {}

Ve JSON ayrıştırılırken kullanın:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);

9

Kotlin için basitçe:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

ya da işte yararlı bir işlev:

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

Ardından, kullanmak için:

val type = typeOfList<YourMagicObject>()

Kodunuzu, reified türleri kullanarak bu işlevi oluşturmak için kullandım: inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!!ve Liste türü ile çağırın:buildType<List<YourMagicObject>>()
coffeemakr

@coffeemakr Burada birleştirilmiş türlere ihtiyacınız yok.
Chad Bingham

Ah. Ama neden bir ArrayList öğesinin tür belirtecini oluşturuyorsunuz buildTypeve işlevi genel türle de çağırıyorsunuz? Bu bir yazım hatası mı? - Bu, ArrayList <ArrayList <YourMagicObject>> oluşturur
coffeemakr

@coffeemakr ah, evet. Yazım hatası
Chad Bingham

7
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Misal:

getList(MyClass[].class, "[{...}]");

İyi bir. Ancak bu DevNG, 2 yıl önce yazılan cevabın yukarısında yer alıyor: stackoverflow.com/a/17300003/1339923 (ve bu yaklaşıma yönelik uyarılar için bu cevabı okuyun)
Lambart

6

Orijinal soruma cevap verdiğinden, doc_180'in cevabını kabul ettim, ancak birisi tekrar bu problemle karşılaşırsa, sorumun 2. yarısını da cevaplayacağım:

Açıkladığım NullPointerError öğesinin List'in kendisi ile değil, içeriği ile ilgisi yoktu!

"Sınıfım" sınıfının "argüman yok" yapıcısı yoktu ve ikisinin de üst sınıfı yoktu. MyClass ve üst sınıfına basit bir "MyClass ()" yapıcısı ekledikten sonra, doc_180 tarafından önerilen Liste serileştirme ve serileştirme dahil her şey yolunda gitti.


1
Soyut sınıflar listeniz varsa aynı hatayı alırsınız. Sanırım bu GSON'un "sınıf başlatılamıyor" için genel hata mesajı.
Drew

Bir yapıcı eklemeyle ilgili ipucu neden tüm null değerlere sahip olduğumu anlamama yardımcı oldu. JSON dizemde "Kime" ve "Kimden" gibi alan adları vardı, ancak nesnemdeki karşılık gelen alanlar "küçük" ve "küçüktür", bu yüzden atlandı
Rune

4

İşte dinamik olarak tanımlanmış bir türle çalışan bir çözüm. Hile, Array.newInstance () öğesini kullanarak uygun dizi türünü oluşturuyor.

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}

Bu yanıt, class.cast()yayınlamanın neden olduğu denetlenmeyen uyarıyı önlemek için kullansaydınız daha iyi olurdu (T). Ya da, daha iyisi, dökümle uğraşmayın ve Arrays.asList()bir diziden a dönüştürmek gibi bir şey kullanmayın List<T>. Ayrıca, bir uzunluğu geçmeye gerek yok Array.newInstance()- sıfır büyüklüğünde bir dizi çağrılacak kadar iyi olacaktır getClass().
Daniel Pryden

Öneri için teşekkürler, önerilen değişikliklerinizi yaptım ve yukarıdaki yazıyı güncelledim.
David Wood

2

Bir olasılık daha eklemek istiyorum. TypeToken kullanmak istemiyorsanız ve json objects dizisini bir ArrayList'e dönüştürmek istiyorsanız, şu şekilde devam edebilirsiniz:

Json yapınız şöyle ise:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

ve sınıf yapınız şöyle:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

o zaman şöyle ayrıştırabilirsiniz:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

Artık className nesnesinin her öğesine erişebilirsiniz.


1

Gson'un 'Tip' sınıfı anlayışı için örnek 2'ye bakın.

Örnek 1: Bu deserilizeResturant'da Employee [] dizisini kullandık ve ayrıntıları aldık

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

Örnek 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}

0

Kays1'in cevabını beğendim ama uygulayamadım. Bu yüzden konseptini kullanarak kendi versiyonumu yaptım.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

Kullanımı:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);

Tabii ki derleme zamanında T kullanmaya çalıştığınız için bu işe yaramaz. Bu etkin bir StringMap Listesinin serisini kaldırır, değil mi?
JHH

0

Benim durumumda @ uncaught_exceptions cevabı işe yaramadı, List.classyerine kullanmak zorunda kaldım java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.