J’écris un code qui va être utilisé pour récupérer des ressources d’un site web. Tout cela ressemble à ceci:
public Collection getProjects() { Ssortingng json = getJsonData(methods.get(Project.class)); //Gets a json list, ie [1, 2, 3, 4] Gson gson = new Gson(); Type collectionType = new TypeToken<Collection>() {}.getType(); return gson.fromJson(json, collectionType); }
Alors naturellement, j’ai essayé de le résumer en utilisant des génériques Java.
/* * Deserialize a json list and return a collection of the given type. * * Example usage: getData(AccountQuota.class) -> Collection */ @SuppressWarnings("unchecked") public Collection getData(Class cls) { Ssortingng json = getJsonData(methods.get(cls)); //Gets a json list, ie [1, 2, 3, 4] Gson gson = new Gson(); Type collectionType = new TypeToken<Collection>(){}.getType(); return (Collection) gson.fromJson(json, collectionType); }
La version générique du code ne fonctionne cependant pas tout à fait.
public void testGetItemFromGetData() throws UserLoginError, ServerLoginError { Map userData = GobblerAuthenticator.authenticate("[email protected]", "mypassword"); Ssortingng client_key = userData.get("client_key"); GobblerClient gobblerClient = new GobblerClient(client_key); ArrayList machines = new ArrayList(); machines.addAll(gobblerClient.getData(Project.class)); assertTrue(machines.get(0).getClass() == Project.class); Log.i("Machine", gobblerClient.getData(Project.class).toSsortingng()); } 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)
La classe en question:
import java.util.Map; public class Project { private int total_bytes_stored; private Ssortingng name; private Ssortingng user_data_guid; private int seqnum; private Ssortingng guid; private Map current_checkpoint; private Map upload_folder; // TODO: schema_version private boolean deleted; // TODO: download_folders public Project() {} // No args constructor used for GSON }
Je ne connais pas bien tous les détails des génériques Java ou des composants internes de GSON et ma recherche n’a pas été particulièrement informative. Il y a un tas de questions ici sur SO, mais la plupart font référence à des méthodes de mise en œuvre comme celle que j’avais. Et les documents GSON ne semblent pas couvrir ce cas particulier. Encore une fois, comment puis-je utiliser Google GSON pour désérialiser un tableau JSON en une Collection d’un type générique?
Malheureusement, vous ne pouvez pas. Le type générique a été effacé par le compilateur et Gson ne dispose d’aucun moyen de savoir quels types vous avez ou quels sont leurs champs.
Je pense que tu peux! Dans le guide de l’utilisateur Gson :
Vous pouvez résoudre ce problème en spécifiant le type paramétré correct pour votre type générique. Vous pouvez le faire en utilisant la classe TypeToken.
Cela signifie qu’en théorie, vous pourriez envelopper votre carte comme suit:
Type t = new TypeToken
Ou (dans mon cas) je devais envelopper une liste comme ceci:
Type t = new TypeToken>() {}.getType(); List list = (List ) new Gson().fromJson("json", t); for (SearchResult r : list) { System.out.println(r); }
(L’exception “java.lang.ClassCastException: com.google.gson.internal.SsortingngMap ne peut pas être convertie en my.SearchResult” .)
J’ai utilisé JsonReader pour désérialiser la chaîne JSON comme suit.
public class JSONReader { ..... private Class persistentClass; public Class getPersistentClass() { if (persistentClass == null) { this.persistentClass = (Class ) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } return persistentClass; } public List read(Reader reader) throws IOException { JsonReader jsonReader = new JsonReader(reader); List objs = new ArrayList (); jsonReader.beginArray(); while (jsonReader.hasNext()) { T obj = (new Gson()).fromJson(jsonReader, getPersistentClass()); if (logger.isFine()) { logger.fine(obj.toSsortingng()); } objs.add(obj); } jsonReader.endArray(); jsonReader.close(); return objs; } ..... }
Vous pouvez appeler la méthode ci-dessus avec les instructions ci-dessous (convertissez une chaîne Json en SsortingngReader et transmettez l’object SsortingngReader à la méthode):
public class Test extends JSONReader { ..... public List parse(Ssortingng jsonSsortingng) throws IOException { SsortingngReader strReader = new SsortingngReader(jsonSsortingng); List objs = read(strReader); return objs; } ..... }
Si tout va bien peut fonctionne!
Le type le plus générique auquel je puisse penser en Java est Map
pour représenter un object JSON et List
pour représenter un tableau d’objects JSON.
Il est très simple d’parsingr cela à l’aide de la bibliothèque GSON (j’ai utilisé la version 2.2.4 au moment de la rédaction):
import com.google.gson.Gson; import java.util.List; import java.util.Map; public class Test { public static void main(Ssortingng[] args) { Ssortingng json = "{\"name\":\"a name\"}"; Gson GSON = new Gson(); Map parsed = GSON.fromJson(json, Map.class); System.out.println(parsed.get("name")); Ssortingng jsonList = "[{\"name\":\"a name\"}, {\"name\":\"a name2\"}]"; List