简体   繁体   English

使用 Java 中的输入流提取特定路径下的 Json 嵌套对象

[英]Extract Json nested objects under a specific path using inputstream in Java

Have been searching this for a while, and I've also developed something working but wondering if there is a framework that can make my life easier.已经搜索了一段时间,我也开发了一些可行的东西,但想知道是否有一个框架可以让我的生活更轻松。

The problem is simple, I have a Json InputStream representing a really large data payload.问题很简单,我有一个 Json InputStream 代表一个非常大的数据有效负载。

I know for sure that this payload contains an array of objects under a known path, and I do not want to parse the stream in memory, instead I would like to seek on the file on the given path and extract all the objects of the nested array one at aa time as a Map<String,Object> .我确定此有效负载包含已知路径下的对象数组,并且我不想解析 memory 中的 stream,而是想在给定路径上的文件中seek并提取嵌套的所有对象一次一个数组作为Map<String,Object>

Example:例子:

{
   "store": {
      "book" : [

         {
           "isbn": "123",
           "author": "author",
           "title": "title",
         },
         ..... many more objects
      ]
   }
}

What I need is to seek on $.store.book and extract individual nested objects for further processing.我需要的是在$.store.bookseek并提取单个嵌套对象以进行进一步处理。

I've tried JsonPath (jayway) but the parse method seem to load everything in memory.我已经尝试过JsonPath (jayway) ,但解析方法似乎加载了 memory 中的所有内容。

I've then used Jackson with the stream library but the solution that I got is a bit convoluted.然后我将 Jackson 与 stream 库一起使用,但我得到的解决方案有点复杂。 Is there an easier way to achieve this?有没有更简单的方法来实现这一点?

Thanks谢谢

One option would be to sequentially look through the data at a limited rate such as 512 or 1024 bytes at a time.一种选择是以有限的速率顺序查看数据,例如一次 512 或 1024 个字节。 You could then parse the bytes to the encoded format which is likely ISO-8859-1 or UTF-8.然后,您可以将字节解析为可能是 ISO-8859-1 或 UTF-8 的编码格式。 You could then read all bytes until some delimiter is met, likely ] to signify the end of the array (hopefully).然后,您可以读取所有字节,直到遇到某个分隔符,可能]表示数组的结尾(希望如此)。 You could then use Jackson or Gson to load just that array of objects.然后,您可以使用 Jackson 或 Gson 仅加载该对象数组。

You need ParamTOFilterBy and FilterValue您需要ParamTOFilterByFilterValue

If you insist on Using JSONPath the selection would look like this:如果您坚持使用JSONPath ,则选择将如下所示:

JsonPath.read(jsonAsString, "$.store.book[?(@.ParamTOFilterBy==FilterValue)]")

This question talks about filtering a JSON String.这个问题谈论过滤 JSON 字符串。 Opinion I like Jackson implementation in this answer意见我喜欢这个答案中的 Jackson 实现

If the JSON array you're interested in can be identified by a JSON pointer, then a FilteringParserDelegate with a JsonPointerBasedFilter can do the job.如果您感兴趣的 JSON 数组可以通过 JSON 指针来识别,那么带有JsonPointerBasedFilterFilteringParserDelegate就可以完成这项工作。 This is basically a streaming parser that skips ahead until the target is found.这基本上是一个流式解析器,它会向前跳过直到找到目标。 Then you can continue streaming the interesting tokens, or do data binding.然后您可以继续流式传输有趣的令牌,或进行数据绑定。

private static final ObjectMapper mapper = new ObjectMapper();
private static final JsonFactory factory = mapper.getFactory();

public static void processArrayElements(InputStream json, 
                                        JsonPointer pointerToArray,
                                        Consumer<Map<String, Object>> consumer)
    throws IOException {

  JsonParser parser = new FilteringParserDelegate(
      factory.createParser(json),
      new JsonPointerBasedFilter(pointerToArray), false, false);

  if (parser.nextToken() != JsonToken.START_ARRAY) {
    throw new IOException("Expected an array but found " + parser.currentToken());
  }

  while (parser.nextToken() != JsonToken.END_ARRAY) {
    consumer.accept(parser.readValueAs(Map.class));
  }
}

To print the books:要打印书籍:

JsonPointer bookArray = JsonPointer.compile("/store/book");
processArrayElements(json, bookArray, System.out::println);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM