2012-04-27 8 views
10

私はウェブサイトからリソースを取り出すために使用されるいくつかのコードを書いています。それはすべてこのような外観です:Google GSONを使用してJSON配列をデシリアライズして、汎用タイプのコレクションにするにはどうすればよいですか?

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

自然に私は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); 
} 

コードの一般的なバージョンではうまくいかない。問題の

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) 

クラス :

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 
} 

は、私は、JavaのジェネリックまたはGSONの内部のすべての詳細と非常に慣れていないんだと、私の検索では、特に有益されていません。ここにはたくさんの質問がありますが、大部分は私が持っていた元のもののようなメソッドを実装することを指しています。そして、GSON docsはこの特定のケースをカバーしていないようです。もう一度、Google GSONを使用してJSON配列をデシリアライズして、ジェネリック型のコレクションにするにはどうすればよいですか?

答えて

8

悲しいことに、あなたはできません。ジェネリック型はコンパイラによって消去されています.Gsonがあなたのタイプやフィールドの種類を知る方法はありません。

+0

聞いて良いが。トピックが多少関わっていると思いますが、理由を簡単に説明できますか? –

+1

JVMは、後方互換性と実装を単純化するため、実行時に型パラメータの背後にある型を認識しません。通常は気にしませんが、これはあなたが行う状況の1つです。ウェブには、より詳細な「Java消去」に関する記事がたくさんあります。 –

+0

実際には、@ jevonによって与えられた答えをチェックすることができます。http://stackoverflow.com/a/12576189/608805 – raukodraug

32

私はあなたができると思います! From the Gson user guide

この問題は、ジェネリックタイプに正しいパラメータ化タイプを指定することで解決できます。これを行うには、TypeTokenクラスを使用します。

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

かを(私の場合)私はそうのようなリストをラップする必要がありました:

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

この

では、理論的には、そのようなあなたの地図を包むことができることを意味し "java.lang.ClassCastException:com.google.gson.internal.StringMapをmy.SearchResultにキャストすることができません"

+0

はい、それは私のためにうまくいきます。しかし、上記のコードのProguardを使用する難読化では、 "匿名クラス(List )が見つかりません"を取得する。私は何かが足りないのですか?それ以外の場合は、proguard設定ファイルで匿名クラスを保持するためのコードを変更または追加する必要があります。 – Karthick

5

私はJsonReaderを使用してjson文字列を次のように逆シリアル化しました。

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

    ..... 
} 

うまくいけば、することができます作品:あなたは文(にStringReaderにJSON文字列に変換し、メソッドににStringReaderオブジェクトを渡す)、以下の方法の使用上呼び出すことができます

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

    ..... 
} 

+0

非常に便利な選択肢! –

1

私が考えることができる最も一般的なタイプは、JSONオブジェクトを表すMap<String, Object>とJSONオブジェクトの配列を表すList<Map<String, Object>>です。

このGSONライブラリを使用して(私が書いている時点でバージョン2.2.4を使用)を解析することは非常に簡単です:ちょうど閉鎖を与える場合、

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")); 
     } 
    } 
} 
関連する問題