2012-04-27 20 views
10

Tôi đang viết một số mã sẽ được sử dụng để truy xuất tài nguyên từ một trang web. Tất cả đều trông như thế này:Tôi làm cách nào để sử dụng Google GSON để deserialize một mảng JSON thành một Bộ sưu tập của một loại chung?

public Collection<Project> getProjects() { 
     String json = getJsonData(methods.get(Project.class)); //Gets a json list, ie [1, 2, 3, 4] 
     Gson gson = new Gson(); 
     Type collectionType = new TypeToken<Collection<Project>>() {}.getType(); 
     return gson.fromJson(json, collectionType); 
    } 

Vì vậy, tự nhiên tôi đã thử abstracting nó bằng cách sử dụng Java Generics.

/* 
    * Deserialize a json list and return a collection of the given type. 
    * 
    * Example usage: getData(AccountQuota.class) -> Collection<AccountQuota> 
    */ 
    @SuppressWarnings("unchecked") 
    public <T> Collection<T> getData(Class<T> cls) { 
     String json = getJsonData(methods.get(cls)); //Gets a json list, ie [1, 2, 3, 4] 
     Gson gson = new Gson(); 
     Type collectionType = new TypeToken<Collection<T>>(){}.getType(); 
     return (Collection<T>) gson.fromJson(json, collectionType); 
} 

Phiên bản chung của mã này không hoạt động.

public void testGetItemFromGetData() throws UserLoginError, ServerLoginError { 
      Map<String,String> userData = GobblerAuthenticator.authenticate("[email protected]", "mypassword"); 
      String client_key = userData.get("client_key"); 
      GobblerClient gobblerClient = new GobblerClient(client_key); 
      ArrayList<Project> machines = new ArrayList<Project>(); 
      machines.addAll(gobblerClient.getData(Project.class)); 
      assertTrue(machines.get(0).getClass() == Project.class); 
      Log.i("Machine", gobblerClient.getData(Project.class).toString()); 
     } 


java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.gobbler.synchronization.Machine 
at com.gobblertest.GobblerClientTest.testGetItemFromGetData(GobblerClientTest.java:53) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169) 
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154) 
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:545) 
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551) 

Lớp trong câu hỏi:

import java.util.Map; 


public class Project { 
    private int total_bytes_stored; 
    private String name; 
    private String user_data_guid; 
    private int seqnum; 
    private String guid; 
    private Map current_checkpoint; 
    private Map<String, String> upload_folder; 
    // TODO: schema_version 
    private boolean deleted; 
    // TODO: download_folders 

    public Project() {} // No args constructor used for GSON 
} 

Tôi không khá quen thuộc với tất cả các chi tiết của Java Generics hoặc internals GSON và tìm kiếm của tôi đã không được đặc biệt thông tin. Có một loạt các câu hỏi ở đây trên SO, nhưng hầu hết chỉ đề cập đến các phương pháp triển khai thực hiện như phương pháp gốc mà tôi có. Và GSON docs dường như không bao gồm trường hợp cụ thể này. Vì vậy, một lần nữa, làm thế nào tôi có thể sử dụng Google GSON để deserialize một mảng JSON vào một Bộ sưu tập của một loại chung?

Trả lời

8

Rất tiếc, bạn không thể. Loại generic đã bị xóa bởi trình biên dịch, và không có cách nào để Gson biết bạn có loại gì hoặc trường của họ là gì.

+0

Tốt để nghe, nếu chỉ để cung cấp cho đóng cửa. Tôi hiểu chủ đề có thể là một chút liên quan, nhưng bạn có thể giải thích tại sao một thời gian ngắn? –

+1

Để tương thích ngược và thực hiện đơn giản, JVM không biết các loại đằng sau các tham số kiểu khi chạy. Thông thường bạn không quan tâm, nhưng đây là một trong những tình huống mà bạn làm. Web có rất nhiều bài viết về "tẩy xoá Java" có nhiều chi tiết hơn. –

+0

Thực ra bạn có thể, kiểm tra câu trả lời được đưa ra bởi @jevon http://stackoverflow.com/a/12576189/608805 – raukodraug

32

Tôi nghĩ bạn có thể! From the Gson user guide:

Bạn có thể giải quyết vấn đề này bằng cách chỉ định loại tham số chính xác cho loại chung của bạn. Bạn có thể làm điều này bằng cách sử dụng lớp TypeToken.

Điều này có nghĩa rằng bạn có thể về mặt lý thuyết, quấn Bản đồ của bạn như vậy:

Type t = new TypeToken<Map<String,Machine>>() {}.getType(); 
Map<String,Machine> map = (Map<String,Machine>) new Gson().fromJson("json", t); 

Hoặc (trong trường hợp của tôi) tôi phải quấn một danh sách như vậy:

Type t = new TypeToken<List<SearchResult>>() {}.getType(); 
List<SearchResult> list = (List<SearchResult>) new Gson().fromJson("json", t); 

for (SearchResult r : list) { 
    System.out.println(r); 
} 

(Tôi đã nhận được ngoại lệ "java.lang.ClassCastException: com.google.gson.internal.StringMap không thể truyền tới my.SearchResult".)

+0

Có, Nó hoạt động tốt cho tôi. Nhưng trong obfuscation sử dụng Proguard của mã trên, Bắt "anonymous class (List ) không tìm thấy". Tôi đang thiếu một cái gì đó? Khác phải thay đổi hoặc thêm mã để giữ cho lớp ẩn danh trong tệp cấu hình proguard. – Karthick

5

Tôi đã sử dụng JsonReader để deserialize chuỗi json như sau.

public class JSONReader<T> { 

    ..... 

    private Class<T> persistentClass; 

    public Class<T> getPersistentClass() { 
     if (persistentClass == null) { 
      this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
     } 
     return persistentClass; 
    } 

    public List<T> read(Reader reader) throws IOException { 
     JsonReader jsonReader = new JsonReader(reader); 
     List<T> objs = new ArrayList<T>(); 
     jsonReader.beginArray();  
     while (jsonReader.hasNext()) { 
      T obj = (new Gson()).fromJson(jsonReader, getPersistentClass()); 
      if (logger.isFine()) { 
       logger.fine(obj.toString()); 
      } 
      objs.add(obj); 
     } 
     jsonReader.endArray(); 
     jsonReader.close(); 
     return objs; 
    } 

    ..... 
} 

Bạn có thể gọi trên sử dụng phương pháp dưới đây báo cáo (chuyển đổi Json chuỗi StringReader, và vượt qua đối tượng StringReader vào phương pháp):

public class Test extends JSONReader<MyClass> { 

    ..... 

    public List<MyClass> parse(String jsonString) throws IOException { 
     StringReader strReader = new StringReader(jsonString); 
     List<MyClass> objs = read(strReader); 
     return objs; 
    } 

    ..... 
} 

Hy vọng rằng có thể các công trình!

+0

Thay thế rất hữu ích! –

1

Loại chung nhất mà tôi có thể nghĩ đến trong Java là Map<String, Object> để thể hiện một đối tượng JSON và List<Map<String, Object>> để biểu diễn một mảng các đối tượng JSON.

Đó là về phía trước rất thẳng để phân tích này sử dụng thư viện GSON (tôi đã sử dụng phiên bản 2.2.4 tại thời điểm viết bài):

import com.google.gson.Gson; 

import java.util.List; 
import java.util.Map; 

public class Test { 
    public static void main(String[] args) { 
     String json = "{\"name\":\"a name\"}"; 
     Gson GSON = new Gson(); 
     Map<String, Object> parsed = GSON.fromJson(json, Map.class); 
     System.out.println(parsed.get("name")); 

     String jsonList = "[{\"name\":\"a name\"}, {\"name\":\"a name2\"}]"; 
     List<Map<String, Object>> parsedList = GSON.fromJson(jsonList, List.class); 
     for (Map<String, Object> parsedItem : parsedList) { 
      System.out.println(parsedItem.get("name")); 
     } 
    } 
} 
Các vấn đề liên quan