GSON +从webservice解析json并获取特定JsonObject的json字符串

时间:2017-03-29 23:00:08

标签: java json gson

我有一个场景,我从webservice读取json。这个Json是一个包含大量数据的节点数组

我想要实现的目标:

  1. 对于数组中的每个节点,只读取所需的元素。使用GSON对象模型完成此操作。
  2. 对于数组中的每个节点,需要该特定节点的Json String,并将其作为String添加到步骤1中的Java Object。
  3. 步骤2是必需的,因为Java impl将调用另一个服务,而该服务又需要每个节点的json以进行进一步处理。

    为了达到这个目的我尝试了什么:

    1. 使用第1步的对象模型创建java对象。
    2. 使用带有JsonArray.class的Object模型再次读取整个json,它为每个节点提供了Json字符串(JsonArray.get(i).toString())。
    3. 找到匹配Json String和Java对象的方法,可能正在使用Node id,这也需要从第2步解析。
    4. 我的问题:

      1. 我使用正确的方法吗?解析Json两次好吗?有没有更好的方法来实现这个目标?
      2. 我并不热衷于使用JsonReader,但这会是一个更好的解决方案吗?如果是这样,如何证明?
      3. staff.json:

        [
        {
            "name": "Joe",
            "age" : 35,
            "position": "dev",
            "salary": 10000,
            "skills":[ "angular", "aws", "java"],
            "Organistaion" : "Test",
            "WorkType": "Full-time"
        },
        
        {
            "name": "John",
            "age" : 29,
            "position": "dev",
            "salary": 10000,
            "skills":[ "python", "c#", "java"],
            "Organistaion" : "Test",
            "WorkType": "Full-time"
        }
        

        Staff.java:

        public class Staff {
        private String name;
        private int age;
        private String staffJson;
        .......
        

        }

        StaffSandbox.java:

        public class StaffSandbox {
        public static void main (String s[]) {
            Gson gson = new Gson();
        
            try (Reader reader = new FileReader("C:\\staff.json")) { // for sample code use file
        
                // Step 1
                List<Staff> staffs = gson.fromJson(reader, new TypeToken<List<Staff>>() {}.getType());
                staffs.forEach(System.out::println);
        
                // Step 2
                String json = new String(Files.readAllBytes(Paths.get(("C:\\staff.json"))));
                JsonArray jsonElement = gson.fromJson(json, JsonArray.class);
                System.out.println( gson.toJson(jsonElement.get(0)).toString());
        
                // Step 3
                for (int i = 0; i < json.size(); i++) {
                    // Read the node Id and match with corresponding Staff object and add the json to it            
                }
        
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        

        }

1 个答案:

答案 0 :(得分:0)

  

我使用正确的方法吗?解析Json两次好吗?有没有更好的方法来实现这一目标?

不是真的。您可以避免两次读取文件+在 第二次读取中节省大量内存:您将整个文件读取为字节数组,首先转换为转换为JSON数组的字符串。对大文件测试这种方法,你可能会OutOfMemoryError

  

我并不热衷于使用JsonReader,但这会是一个更好的解决方案吗?如果是这样,如何证明?

JsonReader以流式方式标记JSON输入,它可以在很多很多情况下帮助你。

如果我理解正确,你只需要这样的东西:

private static void zip(final Gson gson, final Reader reader, final Consumer<? super JsonObject> consumer)
        throws IOException {
    // The next two lines request some stuff from the given Gson instance
    final TypeAdapter<JsonObject> jsonObjectTypeAdapter = gson.getAdapter(JsonObject.class);
    final JsonReader jsonReader = gson.newJsonReader(reader);
    // Ensure that the very first token is array begin `[`
    jsonReader.beginArray();
    // And read all the array elements
    while ( jsonReader.hasNext() ) {
        // JsonReader does not consume entire input stream and can read necessary JSON token only
        final JsonObject incomingJsonObject = jsonObjectTypeAdapter.read(jsonReader);
        // And this is what we generate
        final JsonObject outgoingJsonObject = new JsonObject();
        outgoingJsonObject.add("name", incomingJsonObject.get("name"));
        outgoingJsonObject.add("age", incomingJsonObject.get("age"));
        outgoingJsonObject.add("staffJson", new JsonPrimitive(incomingJsonObject.toString()));
        // Once a "staff row" is ready to use, just delegate it elsewhere
        consumer.accept(outgoingJsonObject);
    }
    jsonReader.endArray();
}

请注意,上面的示例不使用像Staff那样的Java映射。如果需要,可以使用JsonElementGson.toJsonTree()Gson.fromJson()转换为Java映射,反之亦然。此外,您甚至可以将输出直接写入指定的JsonWriter,甚至不创建outgoingJsonObject个实例(普通的R / W管道:从输入读取==&gt;进程==&gt;写入输出)。

使用示例(请注意Reader不能使用两次或更多次;消费者实现使用不同的策略:将每个新生成的JsonObject直接刷新到System.out(您可以处理非常大的这样的数据集;或者将所有这些对象收集到列表然后做你想做的任何事情(集合必须适合内存)):

try ( final Reader reader = getPackageResourceReader(Q43105667.class, "staff.json") ) {
    zip(gson, reader, System.out::println);
}
try ( final Reader reader = getPackageResourceReader(Q43105667.class, "staff.json") ) {
    final Collection<JsonObject> list = new ArrayList<>();
    zip(gson, reader, list::add);
    System.out.println(list);
}
  

{ “名称”: “乔”, “年龄”:35, “staffJson”: “{\” 名称\ “:\” 乔\”,\ “年龄\”:35,\ “位置\”: \ “dev的\”,\ “薪水\”:10000,\ “技能\”:[\ “角\”,\ “AWS \” \ “的java \”],\ “Organistaion \”:\“测试\ “\ ”WorkType \“:\ ”全职\“}”
  { “名”: “约翰”, “年龄”:29, “staffJson”: “{\” 名称\ “:\” 约翰\ “\ ”年龄\“:29 \ ”位置\“:\” 开发\”,\ “工资\”:10000,\ “技能\”:\ “蟒蛇\”,\ “C#\”,\ “的java \”] \ “Organistaion \”:\ “测试\”,\ “WorkType \”:\ “全职\”}“
  [{ “名称”: “乔”, “年龄”:35, “staffJson”: “{\” 名称\ “:\” 乔\ “\ ”年龄\“:35,\ ”位置\“:\” dev的\”,\ “薪水\”:10000,\ “技能\”:[\ “角\”,\ “AWS \” \ “的java \”],\ “Organistaion \”:\ “测试\”, \“WorkType \”:\“Full-time \”}“},{”name“:”John“,”age“:29,”staffJson“:”{\“name \”:\“John \”, \ “年龄\”:29 \ “位置\”:\ “开发\”,\ “工资\”:10000,\ “技能\”:\ “蟒蛇\”,\ “C#\”,\“的java \“],\”Organistaion \“:\”Test \“,\”WorkType \“:\”全职\“}”}}