it-roy-ru.com

Gson Parse Json с массивом с различными типами объектов

Как я могу проанализировать этот JSON с помощью Gson? У меня есть массив с несколькими типами объектов, и я не знаю, какой тип объекта мне нужно создать, чтобы сохранить эту структуру. Я не могу изменить сообщение JSON (я не контролирую сервер).

Единственный класс этой функции (вроде) был

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

    (...)

}

Сообщение JSON (Обратите внимание на массив с несколькими типами объектов.)

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

Руководство пользователя Gson подробно описывает это: 

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

У вас есть объект с полем tr, который представляет собой массив, содержащий произвольные типы.

Руководство пользователя объясняет, что вы не можете напрямую десериализовать такую ​​структуру, и рекомендует:

Используйте API синтаксического анализатора Gson (низкоуровневый потоковый анализатор или анализатор DOM JsonParser), чтобы проанализировать элементы массива, а затем используйте Gson.fromJson () на каждом из элементов массива. Это предпочтительный подход.

В вашем случае ... это действительно будет зависеть от того, какие объекты были возможны в этом массиве. Если у всех будет один и тот же внутренний объект, вы захотите сделать что-то вроде ...

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

И, конечно, это должно быть внутри пользовательского десериализатора для вашего фактического объекта, который будет иметь поля tr и 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;
    }
}

Теперь все готово - вы можете использовать этот десериализатор и создать свой объект:

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

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

Если a, b и т.д. Важны ... ну, вам придется это выяснить. Но вышесказанное поможет вам лучше понять, что будет необходимо для работы с вашей структурой JSON. 

Для полноты картины, единственный «хакерский» способ обойти это, если существует довольно ограниченное количество этих типов, и внутренний объект также довольно ограничен с точки зрения его полей. Вы можете создать POJO, который охватывает все возможности:

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

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

Когда Gson десериализует JSON, он установит все пропущенные поля в ваших POJO на null. Теперь вы можете иметь tr быть List или их массив в вашем POJO. Опять же, чтобы подчеркнуть, это действительно довольно хакерский и неправильный способ сделать это, но я подумал, что объясню, что потребуется для непосредственного анализа этого массива. 

20
Brian Roach

Я выбираю что-то из каждого ответа и делаю это так:

Ответ Объект

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

    (...)
}

Общий Пользователь

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

A

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

B 

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

Метод разбора

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

Это не так элегантно, как я хотел, и смешивать нативные объекты JsonObject с методами Gson, но работает для меня.

4
Laranjeiro

Попробуйте этот код здесь:

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

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

Использование:

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

Как вы видите, мне нужно было изменить структуру:

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

Но, к сожалению, мне не удалось разрешить несколько ключей. Прямо сейчас я понятия не имею, как это исправить.

2
rekire

Я думаю, что эта ссылка может вам помочь: https://sites.google.com/site/gson/gson-user-guide#TOC-Collections-Examples

По сути, создайте класс для вашего «объекта» (вроде пользователя), а затем используйте код десериализации Gson, например так:

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