Google Gson - đối tượng deserialize danh sách <class>? (loại chung)


441

Tôi muốn chuyển một đối tượng danh sách qua Google Gson, nhưng tôi không biết làm thế nào để giải tuần tự các loại chung chung.

Những gì tôi đã thử sau khi xem xét điều này (câu trả lời của BalusC):

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

nhưng sau đó tôi gặp lỗi trong nhật thực với nội dung "Loại danh sách mới () {} phải thực hiện phương thức trừu tượng được kế thừa ..." và nếu tôi sử dụng một bản sửa lỗi nhanh, tôi sẽ nhận được một con quái vật gồm hơn 20 sơ khai phương thức.

Tôi khá chắc chắn rằng có một giải pháp dễ dàng hơn, nhưng dường như tôi không thể tìm thấy nó!

Biên tập:

Bây giờ tôi có

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

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

Tuy nhiên, tôi nhận được ngoại lệ sau tại dòng "fromJson":

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)

Tôi làm JsonParseExceptions bắt và "kết quả" không phải là null.

Tôi đã kiểm tra listType với trình gỡ lỗi và nhận được thông tin sau:

  • loại danh sách
    • args = ListOfTypes
      • danh sách = không
      • yetTypes = Loại [1]
    • trình tải = PathClassLoader
    • chủ sở hữu0 = null
    • ownTypeRes = null
    • rawType = Class (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

vì vậy có vẻ như lệnh gọi "getClass" không hoạt động đúng. Bất kỳ đề xuất...?

Chỉnh sửa2: Tôi đã kiểm tra Hướng dẫn sử dụng Gson . Nó đề cập đến một Ngoại lệ Thời gian chạy sẽ xảy ra trong khi phân tích một loại chung cho Json. Tôi đã làm "sai" (không được hiển thị ở trên), giống như trong ví dụ, nhưng không có ngoại lệ nào cả. Vì vậy, tôi đã thay đổi serialization như trong hướng dẫn sử dụng đề xuất. Không giúp đỡ, mặc dù.

Edit3: Đã giải quyết, xem câu trả lời của tôi dưới đây.


1
Câu trả lời bạn chỉ vào, sử dụng TokenType. Bạn đã thử theo cách đó?
Nishant

chỉ có gợi ý giống như một câu trả lời. lần sau tôi sẽ đưa ra ví dụ gần hơn. ;)
sứa

Bạn có thể thử triển khai danh sách theo loại mã thông báo không? Vì kiểu thô của bạn là danh sách mảng, bạn nên thử danh sách mảng.
unsaught_exceptions

Câu trả lời:


954

Phương pháp khử lưu trữ bộ sưu tập chung:

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

Vì một số người trong các ý kiến ​​đã đề cập đến nó, đây là một lời giải thích về cách TypeTokenlớp đang được sử dụng. Cấu trúc new TypeToken<...>() {}.getType()nắm bắt một kiểu thời gian biên dịch (giữa <>) thành một java.lang.reflect.Typeđối tượng thời gian chạy . Không giống như một Classđối tượng, chỉ có thể đại diện cho một loại thô (đã xóa),Type đối tượng có thể đại diện cho bất kỳ loại nào trong ngôn ngữ Java, bao gồm cả một khởi tạo được tham số hóa của một loại chung.

Bản TypeTokenthân lớp không có hàm tạo công khai, vì bạn không được phép xây dựng nó trực tiếp. Thay vào đó, bạn luôn xây dựng một lớp con ẩn danh (do đó {}, đây là một phần cần thiết của biểu thức này).

Do xóa kiểu, TypeTokenlớp chỉ có thể chụp các kiểu được biết đầy đủ tại thời điểm biên dịch. (Đó là, bạn không thể làm new TypeToken<List<T>>() {}.getType()cho một tham số loại T.)

Để biết thêm thông tin, xem tài liệu cho TypeTokenlớp .


31
Trong các phiên bản mới của GSON, bộ điều khiển TypeToken không công khai, do đó ở đây bạn nhận được hàm tạo không thể nhìn thấy lỗi. Bạn phải làm gì trong trường hợp này?
Pablo

8
Sử dụng phiên bản thực tế của GSON (2.2.4), nó hoạt động trở lại. Bạn có thể truy cập các nhà xây dựng ở đây.

Đối tượng json của tôi bắt đầu bằng một đối tượng, sau đó chứa một mảng của đối tượng tôi muốn { "myObjectArray":[ {....} , {....} , {....} ] }, tôi đã tạo tệp mô hình cho {....}, làm cách nào để lấy mã bộ sưu tập chung này để không giả sử phần tử gốc của tôi là một mảng mà không tạo tệp đối tượng lồng nhau mới
CQM

7
Yêu cầu nhập khẩu --- nhập java.lang.reflect.Type; nhập com.google.gson.reflect.TypeToken
Umair Saleem

4
Điều này là tốt nếu YourClass được cố định trong mã. Điều gì nếu lớp đến trong thời gian chạy?
jasxir

273

Một cách khác là sử dụng một mảng như một kiểu, ví dụ:

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

Bằng cách này, bạn sẽ tránh được mọi rắc rối với đối tượng Type và nếu bạn thực sự cần một danh sách, bạn luôn có thể chuyển đổi mảng thành danh sách bằng cách:

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

IMHO này là dễ đọc hơn nhiều.

Và để làm cho nó trở thành một danh sách thực tế (có thể được sửa đổi, hãy xem giới hạn của Arrays.asList()), sau đó chỉ cần làm như sau:

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

4
điều đó thật tuyệt! Làm thế nào tôi có thể sử dụng nó với sự phản ánh? Tôi không biết MyClassgiá trị và nó sẽ được xác định động!
Amin Sh

1
nota: với điều này, hãy cẩn thận rằng mcList không phải là một danh sách đầy đủ. nhiều thứ sẽ không hoạt động.
njzk2

4
Làm thế nào để sử dụng nó với thuốc generic? T[] yourClassList = gson.fromJson(message, T[].class);// không thể chọn từ biến loại
Pawel Cioch

2
@MateusViccari tại thời điểm nhận xét đó, mcListtrong câu trả lời này chỉ là kết quả của cuộc gọi đến Arrays.asList. Phương thức này trả về một danh sách mà hầu hết các phương thức tùy chọn không được thực hiện và ném ngoại lệ. Chẳng hạn, bạn không thể thêm bất kỳ yếu tố nào vào danh sách đó. Như bản chỉnh sửa sau này cho thấy, Arrays.asListcó những hạn chế và việc đưa nó vào thực tế ArrayListcho phép bạn có được một danh sách hữu ích hơn trong nhiều trường hợp.
njzk2

2
Nếu bạn cần xây dựng một kiểu mảng trong thời gian chạy cho một loại phần tử tùy ý, bạn có thể sử dụng Array.newInstance(clazz, 0).getClass() như được mô tả trong câu trả lời của David Wood .
Daniel Pryden

31

Tham khảo bài viết này. Kiểu Java Chung làm đối số cho GSON

Tôi có giải pháp tốt hơn cho việc này. Đây là lớp trình bao cho danh sách để trình bao bọc có thể lưu trữ chính xác loại danh sách.

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

Và sau đó, mã có thể đơn giản:

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

mEntity.rulePattern
Al Lelopath

Nó chỉ là một đối tượng mẫu để thử nghiệm. Bạn không cần phải quan tâm đến nó. Sử dụng phương thức toList và mọi thứ đều ổn.
Hạnh phúc hơn

@Happier Tôi đang cố gắng thực hiện Gson 2.8.2 này và nó dường như không hoạt động. Bất kỳ cơ hội stackoverflow.com/questions/50743932/, bạn có thể xem và cho tôi biết những gì tôi đang thiếu
Praveen

1
@Praveen Tôi đã thử cách này trong 2.8.2, nó hoạt động như ban đầu.
Hạnh phúc hơn vào

30

Gson 2.8, chúng ta có thể tạo chức năng như

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

Ví dụ sử dụng

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

2
TypeToken#getParameterizedcó vẻ tốt hơn sau đó là vụ hack với một lớp con ẩn danh
Nikolay Kulachenko

đây phải là câu trả lời được chấp nhận; ít nhất là cho các phiên bản mới hơn
ccpizza

2
Đây là câu trả lời hoàn hảo. Giải quyết vấn đề của tôi.
donmj

Tôi đã sao chép phương thức của bạn "nguyên trạng" và nó không hoạt động: trình biên dịch nói "Phương thức getParameterized (Class <List>, Class <T>) không được xác định cho loại TypeToken". Tôi đã kiểm tra cả phiên bản Gson của mình (2.8.0) và tài liệu và mọi thứ đều ổn ở bên này ... Tôi đã kết thúc bằng cách sử dụng giải pháp @Happier hoạt động tốt
đèn chiếu sáng

@leguminator bạn đã nhập TypeToken đúng chưa? và bạn đang sử dụng java hoặc kotlin. Tôi sẽ thử kiểm tra lại
Phan Văn Linh

26

Wep, một cách khác để đạt được kết quả tương tự. Chúng tôi sử dụng nó cho khả năng đọc của nó.

Thay vì làm câu khó đọc này:

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

Tạo một lớp trống mở rộng Danh sách đối tượng của bạn:

public class YourClassList extends ArrayList<YourClass> {}

Và sử dụng nó khi phân tích cú pháp JSON:

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

9

Đối với Kotlin đơn giản là:

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

hoặc, đây là một chức năng hữu ích:

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

Sau đó, để sử dụng:

val type = typeOfList<YourMagicObject>()

Tôi đã sử dụng mã của mình để tạo chức năng này bằng các loại hợp nhất: inline fun <reified T> buildType() = object : TypeToken<T>() {}.type!!và gọi nó bằng loại Danh sách:buildType<List<YourMagicObject>>()
coffeemakr

@coffeemakr Bạn không cần các loại thống nhất ở đây.
Chad Bingham

Oh. Nhưng tại sao bạn tạo mã thông báo loại của ArrayList trong buildTypevà cũng gọi hàm với loại chung? Đây có phải là một lỗi đánh máy? - Điều này sẽ tạo ArrayList <ArrayList <YourMagicObject >>
coffeemakr

@coffeemakr ah, vâng. Typo
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);
}

Thí dụ:

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

Tốt một. Nhưng DevNGcâu trả lời này trùng lặp với câu trả lời trên, được viết 2 năm trước: stackoverflow.com/a/17300003/1339923 (và đọc câu trả lời đó để cảnh giác với phương pháp này)
Lambart

6

Khi nó trả lời câu hỏi ban đầu của tôi, tôi đã chấp nhận câu trả lời của doc_180, nhưng nếu ai đó gặp phải vấn đề này một lần nữa, tôi cũng sẽ trả lời nửa sau câu hỏi của mình:

NullPulumError mà tôi mô tả không liên quan gì đến Danh sách, nhưng với nội dung của nó!

Lớp "MyClass" không có hàm tạo "không đối số" và cũng không có lớp siêu lớp của nó. Khi tôi đã thêm một hàm tạo "MyClass ()" đơn giản vào MyClass và siêu lớp của nó, mọi thứ đều hoạt động tốt, bao gồm cả tuần tự hóa và giải tuần tự hóa theo đề xuất của doc_180.


1
Nếu bạn có một danh sách các lớp trừu tượng, bạn sẽ gặp cùng một lỗi. Tôi đoán đây là thông báo lỗi chung của GSON cho "Không thể khởi tạo lớp".
vẽ

Mẹo về việc thêm một hàm tạo đã giúp tôi nhận ra lý do tại sao tôi có tất cả các giá trị null. Tôi có các tên trường như "Tới" và "Từ" trong chuỗi JSON của mình, nhưng các trường tương ứng trong đối tượng của tôi là "đến" và "từ" trong chữ thường, vì vậy chúng bị bỏ qua
Rune

4

Đây là một giải pháp hoạt động với một loại xác định động. Thủ thuật là tạo ra kiểu mảng thích hợp bằng Array.newInstance ().

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

Câu trả lời này sẽ tốt hơn nếu bạn sử dụng class.cast()để tránh cảnh báo không được kiểm tra do truyền tới (T). Hoặc, tốt hơn nữa, đừng bận tâm với việc truyền và sử dụng một cái gì đó như Arrays.asList()để chuyển đổi từ một mảng sang a List<T>. Ngoài ra, không cần phải vượt qua độ dài đến Array.newInstance()- một mảng có kích thước bằng 0 sẽ đủ tốt để gọi getClass()tiếp.
Daniel Pryden

Cảm ơn đề xuất, tôi đã thực hiện các thay đổi được đề xuất của bạn và cập nhật bài viết ở trên.
David Wood

2

Tôi muốn thêm một khả năng nữa. Nếu bạn không muốn sử dụng TypeToken và muốn chuyển đổi mảng đối tượng json thành ArrayList, thì bạn có thể tiến hành như sau:

Nếu cấu trúc json của bạn giống như:

{

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

}

và cấu trúc lớp của bạn giống như:

public class ClassName implements Parcelable {

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

sau đó bạn có thể phân tích nó như sau:

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

Bây giờ bạn có thể truy cập từng phần tử của đối tượng className.


1

Tham khảo ví dụ 2 để hiểu về lớp 'Loại' của Gson.

Ví dụ 1: Trong deserilizeResturant này, chúng tôi đã sử dụng mảng Employee [] và nhận thông tin chi tiết

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

Ví dụ 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

Tôi thích câu trả lời từ kays1 nhưng tôi không thể thực hiện nó. Vì vậy, tôi đã xây dựng phiên bản của riêng mình bằng cách sử dụng khái niệm của mình.

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

Sử dụng:

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

Chắc chắn điều này không thể hoạt động vì bạn đang cố gắng sử dụng T trong thời gian biên dịch. Điều này sẽ giải thích hiệu quả cho Danh sách StringMap, phải không?
JHH

0

Trong câu trả lời của tôi @ unsaught_exceptions không hoạt động, tôi phải sử dụng List.classthay vì java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.