【问题标题】:Java Gson parse Json object to arrayJava Gson 将 Json 对象解析为数组
【发布时间】:2020-04-03 10:16:42
【问题描述】:

我正在尝试使用 Java 中的 Gson 库解析下面的 Json。当使用其他语言(例如 C#)时,此 JSON 被解析为一个数组,但 Gson 似乎将其转换为一组 java 属性(老实说,这对我来说更有意义)。有谁知道我是否可以改变 Gson lib 的这种行为?

{
  "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

//编辑 我以这个当前的简单 Json 为例,但是我对这段代码的应用将是解析形状不同且未知的 Json。因此,我需要 Gson 自动将其解析为数组,以便 isJsonArray 返回 true

【问题讨论】:

  • 其实你的 JSON 不是一个数组,它是一个对象内部的一个数组。 isJsonArray() 用 String json = "\"[Outer\": { \ [...] }] 返回什么?
  • 那将是一个数组。有人告诉我,上面的示例将由 JavaScript 和 c# lib 解析为数组,我正在尝试使用 Gson 在 java 中复制该行为。
  • 也许我不理解你,但据我所知,Js 不会将你的示例 json 解析为数组,而是解析为具有两个参数的对象:imgur.com/vxL043G
  • 我同意@CarlosLópezMarí,但显然 JavaScript 和 c# 将其解析为一个数组,我试图在 Java 中模仿它。
  • 我粘贴的图片来自 Javascript。

标签: java arrays json gson


【解决方案1】:

TL;DR:请参阅底部的“使用反序列化器”部分以直接解析为数组。


那个 JSON 不包含任何数组。数组将使用 [...] JSON 语法。

通常,JSON 对象会映射到 POJO,名称/值对中的 name 会映射到 POJO 的字段。

不过,JSON 对象也可以映射到 Map,这在名称为动态时特别有用,因为 POJO 字段是静态的。

使用地图

以数值为名称的 JSON 对象可以映射到 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;
}

测试

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

输出

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

使用反序列化器

但是,如果在解析时完成转换为数组可能会更有用,因此您需要编写一个JsonDeserializer 并使用@JsonAdapter 告诉 Gson:

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]]]

【讨论】:

    【解决方案2】:

    我假设您的 JsonObject 是一个 POJO 类,例如:

    public Inner[] outer;

    如果您想要一个对象数组,您可以将代码更改为:

    Inner[] jo = gson.fromJson(json, Inner[].class);
    

    【讨论】:

    • 感谢 Carlos,我已更新我的帖子以尝试进一步解释。
    【解决方案3】:
    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());
            }
    

    您可以使用 JsonNode 类的方法 findParent findValue 和 findPath,与其他解析库相比,这会减少您的代码。

    【讨论】:

      【解决方案4】:

      请参考以下代码 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);
      

      【讨论】:

      • 谢谢 tir0w。不幸的是,我不知道我将要解析的 JSON 的形状,所以我无法专门为案例编写代码。我需要一种方法来了解上述“数组”场景是否发生在形状未知的 JSON 文件中。例如。属性“outer”可能不存在,或者可能在另一个级别。我目前依靠 isJsonArray 来确定它是否是一个数组以及我试图改变的这个函数的行为。
      • @JAC2703,好吧,它不是一个数组,这就是你没有得到一个数组的原因。我认为你的方法是正确的,但你的例子不是。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-30
      • 1970-01-01
      相关资源
      最近更新 更多