Java Gson parse Json объект в массив - PullRequest
1 голос
/ 03 апреля 2020

Я пытаюсь разобрать приведенное ниже Json, используя библиотеку Gson в Java. При использовании других языков, таких как C#, этот JSON анализируется в массив, однако кажется, что Gson преобразует это в набор атрибутов java (что, честно говоря, имеет больше смысла для меня). Кто-нибудь знает, могу ли я изменить это поведение библиотеки Gson?

{
  "Outer": {
    "0": {
      "Attr1": 12345,
      "Attr2": 67890
    },
    "1": {
      "Attr1": 54321,
      "Attr2": 09876
    }
  }
}

Приведенный ниже код демонстрирует, как Gson анализирует массив как JsonObject. Чтобы было ясно, я понимаю, что упомянул outer как JsonObject, но я просто делал это, чтобы продемонстрировать код. Если я попытаюсь сослаться на outer как JsonArray, код завершится неудачей.

String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .setLenient()
            .serializeNulls()
            .create();

JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");

System.out.println(outer);
System.out.println(outer.isJsonArray());

Результат:

{"0":{"Attr1":12345,"Attr2":67890},"1":{"Attr1":54321,"Attr2":"09876"}}
false

// edit Я использую этот текущий простой Json в качестве примера, однако мое применение этого кода будет состоять в разборе Json, который имеет различную и неизвестную форму. Поэтому мне нужно, чтобы Gson автоматически проанализировал этот массив, чтобы isJsonArray вернул true.

Ответы [ 4 ]

2 голосов
/ 03 апреля 2020

TL; DR: См. Раздел «Использование десериализатора» в нижней части для разбора прямо в массив.


Что JSON не содержит любые массивы. Массив будет использовать синтаксис [...] JSON.

Обычно объект JSON сопоставляется с POJO, а name в парах имя / значение отображается в поле POJO .

Однако объект JSON также можно сопоставить с Map, что особенно полезно, когда имена являются динамическими c, так как поля POJO имеют статус c.

Использование карты

Объект JSON со значениями числительных c в качестве имен можно сопоставить с Map<Integer, ?>, например, для анализа этих JSON в POJO, сделайте это следующим образом:

class Root {
    @SerializedName("Outer")
    public Map<Integer, Outer> outer;
    @Override
    public String toString() {
        return "Root[outer=" + this.outer + "]";
    }
}
class Outer {
    @SerializedName("Attr1")
    public int attr1;
    @SerializedName("Attr2")
    public int attr2;
    @Override
    public String toString() {
        return "Outer[attr1=" + this.attr1 + ", attr2=" + this.attr2 + "]";
    }
}

Тест

Gson gson = new GsonBuilder().create();
Root root;
try (BufferedReader in = Files.newBufferedReader(Paths.get("test.json"))) {
    root = gson.fromJson(in, Root.class);
}
System.out.println(root);

Выход

Root[outer={0=Outer[attr1=12345, attr2=67890], 1=Outer[attr1=54321, attr2=9876]}]

Получить как массив

Затем вы можете добавить вспомогательный метод к классу Root, чтобы получить его в виде массива:

public Outer[] getOuterAsArray() {
    if (this.outer == null)
        return null;
    if (this.outer.isEmpty())
        return new Outer[0];
    int maxKey = this.outer.keySet().stream().mapToInt(Integer::intValue).max().getAsInt();
    Outer[] arr = new Outer[maxKey + 1];
    this.outer.forEach((k, v) -> arr[k] = v);
    return arr;
}

Test

System.out.println(Arrays.toString(root.getOuterAsArray()));

Вывод

[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]

Использование десериализатора

Однако, вероятно, было бы более полезно, если преобразование в массив выполняется во время синтаксического анализа, поэтому вам нужно написать JsonDeserializer и скажите Gson об этом, используя @JsonAdapter :

class Root {
    @SerializedName("Outer")
    @JsonAdapter(OuterArrayDeserializer.class)
    public Outer[] outer;

    @Override
    public String toString() {
        return "Root[outer=" + Arrays.toString(this.outer) + "]";
    }
}
class OuterArrayDeserializer implements JsonDeserializer<Outer[]> {
    @Override
    public Outer[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Parse JSON array normally
        if (json.isJsonArray())
            return context.deserialize(json, Outer[].class);

        // Parse JSON object using names as array indexes
        JsonObject obj = json.getAsJsonObject();
        if (obj.size() == 0)
            return new Outer[0];
        int maxKey = obj.keySet().stream().mapToInt(Integer::parseInt).max().getAsInt();
        Outer[] arr = new Outer[maxKey + 1];
        for (Entry<String, JsonElement> e : obj.entrySet())
            arr[Integer.parseInt(e.getKey())] = context.deserialize(e.getValue(), Outer.class);
        return arr;
    }
}

Тот же класс Outer и тестовый код, что и выше.

Выход

Root[outer=[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]]
0 голосов
/ 03 апреля 2020
Jackson – Marshall String to JsonNode will be useful in your case.with following pom:- 


    //POM FILE


        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

//JAVA CODE
    //read json file data to String


     byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

        //create ObjectMapper instance
        ObjectMapper objectMapper = new ObjectMapper();

        //read JSON like DOM Parser
        JsonNode rootNode = objectMapper.readTree(jsonData);
        JsonNode idNode = rootNode.path("id");
        System.out.println("id = "+idNode.asInt());

        JsonNode phoneNosNode = rootNode.path("phoneNumbers");
        Iterator<JsonNode> elements = phoneNosNode.elements();
        while(elements.hasNext()){
            JsonNode phone = elements.next();
            System.out.println("Phone No = "+phone.asLong());
        }

Вы можете использовать методы findParent findValue и findPath класса JsonNode, которые уменьшают ваш код по сравнению с другой библиотекой синтаксического анализа.

0 голосов
/ 03 апреля 2020

Пожалуйста, обратитесь к приведенному ниже коду 1. Чтобы получить массив объектов (outerArray) 2. Вы можете извлечь JsonArray (outerJsonArray), содержащий значения внутренних объектов в Outer (если ключи не важны для дальнейшего использования )

String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().serializeNulls().create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");

Object[] outerArray = outer.entrySet().toArray();
// outerArray: [0={"Attr1":12345,"Attr2":67890}, 1={"Attr1":54321,"Attr2":"09876"}]

JsonArray outerJsonArray = new JsonArray();
outer.keySet().stream().forEach(key -> {
outerJsonArray.add(outer.get(key));
});
//jsonArray=[{"Attr1":12345,"Attr2":67890},{"Attr1":54321,"Attr2":"09876"}]

System.out.println(outer);
System.out.println(outerJsonArray.isJsonArray() + " " + outerJsonArray);
0 голосов
/ 03 апреля 2020

Я предполагаю, что ваш JsonObject является классом POJO, таким как:

public Inner[] outer;

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

Inner[] jo = gson.fromJson(json, Inner[].class);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...