web-dev-qa-db-fra.com

Gson Parse Json avec tableau avec différents types d'objet

Comment analyser ce JSON avec Gson? J'ai un tableau avec plusieurs types d'objets et je ne sais pas quel type d'objet je dois créer pour enregistrer cette structure. Je ne peux pas changer le message JSON (je ne contrôle pas le serveur).

La seule classe qui fonctionne (en quelque sorte) est-ce

public class Response {
    private List<Object> tr;
    private int results;

    (...)

}

Message JSON (Notez le tableau avec plusieurs types d'objet.)

{
   "tr":
   [
       {
           "a":
           {
               "userId": "112"
           }
       },
       {
           "b":
           {
               "userId": "123",
               "address":"street dummy" 
           }
       },
       {
           "a":
           {
               "userId": "154"
           }
       }
   ],
"results":3
}
15
Laranjeiro

Le Guide de l'utilisateur Gson couvre explicitement ceci: 

https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Collection-with-Objects-of-Arbitrary-Types

Vous avez un objet avec un champ tr qui est un tableau contenant des types arbitraires.

Le guide de l'utilisateur explique qu'il est impossible de désérialiser directement une telle structure et recommande:

Utilisez l’API d’analyseur de Gson (analyseur de flux continu de bas niveau ou l’analyseur DOM JsonParser) pour analyser les éléments du tableau, puis utilisez Gson.fromJson () Sur chacun des éléments du tableau. C'est l'approche privilégiée.

Dans votre cas, cela dépend vraiment des objets possibles dans ce tableau. S'ils vont tous avoir le même objet intérieur, vous voudrez faire quelque chose comme ...

List<MyUserPojo> list = new ArrayList<MyUserPojo>();
JsonArray array = parser.parse(json).getAsJsonObject().getAsJsonArray("tr");
for (JsonElement je : array)
{
    Set<Map.Entry<String,JsonElement>> set = je.getAsObject().entrySet();
    JsonElement je2 = set.iterator().next().getValue();

    MyUserPojo mup = new Gson().fromJson(je2, MyUserPojo.class);
    list.add(mup);
}

Et bien sûr, cela devrait être dans un désérialiseur personnalisé pour votre objet réel qui aurait les champs tr et results.

class MyPojo
{
    List<MyUserPojo> userList;
    int results; 
}

class MyUserPojo
{
    String userId;
    String address;
}

class MyDeserializer implements JsonDeserializer<MyPojo>
{
    @Override
    public MyPojo deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
                              throws JsonParseException
    {
        List<MyUserPojo> list = new ArrayList<MyUserPojo>();
        JsonArray array = je.getAsJsonObject().getAsJsonArray("tr");
        for (JsonElement je2 : array)
        {
            Set<Map.Entry<String,JsonElement>> set = je2.getAsObject().entrySet();
            JsonElement je3 = set.iterator().next().getValue();                 

            MyUserPojo mup = new Gson().fromJson(je3, MyUserPojo.class);
            list.add(mup);
        }

        MyPojo mp = new MyPojo();
        mp.tr = list;
        mp.results = je.getAsObject().getAsJsonPrimitive("results").getAsInt();

        return mp;
    }
}

Maintenant tout est prêt - vous pouvez utiliser ce désérialiseur et créer votre objet:

Gson gson = new GsonBuilder()
                .registerTypeAdapter(MyPojo.class, new MyDeserializer())
                .build();

MyPojo mp = gson.fromJson(json, MyPojo.class);

Si les a, b etc sont importants ... eh bien, vous devrez comprendre cela. Mais ce qui précède devrait vous aider à comprendre ce qui sera nécessaire pour gérer votre structure JSON. 

Par souci d'exhaustivité, la seule solution "simpliste" consiste à utiliser un nombre assez limité de ces types et à ce que l'objet interne soit également assez limité en termes de champs. Vous pouvez créer un POJO qui englobe toutes les possibilités:

class MyPojo 
{
    MySecondPojo a;
    MySecondPojo b;
    ...
    MySecondPojo f;
}

class MySecondPojo
{
    String userId;
    String address;
    ...
    String someOtherField;
}

Lorsque Gson désérialise JSON, les champs manquants de vos POJO sont définis sur null. Vous pouvez maintenant avoir tr être un List ou un tableau de ceux-ci dans votre POJO. Encore une fois et pour souligner, c’est vraiment assez hacky et la mauvaise façon de le faire , mais je pensais expliquer ce qu’il faudrait faire pour analyser directement ce tableau. 

20
Brian Roach

Je choisis quelque chose dans chaque réponse et je l'ai fait comme suit:

Réponse Objet

public class Response {
    private List<Users> tr;
    private int results;

    (...)
}

Générique Utilisateur

public class User {
    public static final int TYPE_USER_A =0;
    public static final int TYPE_USER_B =1;
    private String userId;
    private int type;
    (...)
}

UNE

public class a extends User {
    private String location;
    (...) 
}

B 

public class b extends User { 
    private String adress;   
    (...)
}

Méthode d'analyse

private Response buildResponseObject(String response) {
        Response tls = new Response();
        List<Users> users = new ArrayList<users>();
        User u;

        try {
            JSONObject object = new JSONObject(response);
            tls.setResults(object.getInt("results"));
            JSONArray array = object.getJSONArray("tr");

            for (int i = 0; i < array.length(); i++) {
                JSONObject trs = array.getJSONObject(i);

                if (trs.has("a")) {
                    String json = trns.getString("a");
                    A a = new Gson().fromJson(json,A.class);
                    a.setType(User.TYPE_USER_A);
                    users.add(a);

                } else if (trs.has("b")) {
                    String json = trs.getString("b");
                    B b= new Gson().fromJson(json,B.class);
                    B.setType(User.TYPE_USER_B);
                    users.add(b);
                }
            }

            tls.setUsers(users);

        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return tls;
    }

Ce n'est pas aussi élégant que je voulais et mélanger JsonObjects natif avec les méthodes Gson, mais fonctionne pour moi.

4
Laranjeiro

Essayez ce code ici:

public class Address {
    public String userId;
    public String address;
    // ...
}

public class Response {
    private HashMap<String, Address> tr;
    private int results;
    // ...
}

Usage:

String json = "{\n  \"tr\":\n  {\n    \"a\": {\n       \"userId\": \"112\"\n    },\n    \"b\": {\n       \"userId\": \"123\",\n       \"address\":\"street dummy\"\n    },\n    \"c\": {\n       \"userId\": \"154\"\n    }\n  },\n  \"results\":3\n}";

Response users = new Gson().fromJson(json, Response.class);

Comme vous pouvez le constater, j'ai eu besoin de modifier la structure:

{
  "tr":
  {
    "a": {
       "userId": "112"
    },
    "b": {
       "userId": "123",
       "address":"street dummy"
    },
    "c": {
       "userId": "154"
    }
  },
  "results":3
}

Mais malheureusement, je ne parviens pas à autoriser plusieurs clés. À l'heure actuelle, je n'ai aucune idée de comment résoudre ce problème.

2
rekire

Je pense que ce lien pourrait vous aider: https://sites.google.com/site/gson/gson-user-guide#TOC-Collections-Examples

Fondamentalement, créez une classe pour votre "objet" (genre d'utilisateur, je suppose), puis utilisez le code de désérialisation de Gson, comme ceci:

Type collectionType = new TypeToken<Collection<User>>(){}.getType();
Collection<User> users= gson.fromJson(json, collectionType);
0
Aerilys