是否有适用于 Java 的事件驱动 JSON REST 客户端 API?

2022-09-01 17:44:29

我有一个Java应用程序,它使用Spring的RestTemplate API来编写JSON REST服务的简洁,可读的消费者:

基本上:

 RestTemplate rest = new RestTemplate(clientHttpRequestFactory);
 ResponseEntity<ItemList> response = rest.exchange(url,
            HttpMethod.GET,     
            requestEntity,
            ItemList.class);

 for(Item item : response.getBody().getItems()) {
        handler.onItem(item);
 }

JSON 响应包含一个项目列表,如您所见,我在自己的代码中有一个事件驱动的设计,用于依次处理每个项目。但是,整个列表作为 的一部分在内存中,该生成。responseRestTemplate.exchange()

我希望应用程序能够处理包含大量项目的响应 - 例如50,000,在这种情况下,实现存在两个问题:

  1. 在传输整个 HTTP 响应之前,不会处理单个项目 - 增加了不必要的延迟。
  2. 巨大的响应对象位于内存中,在处理完最后一项之前无法进行 GC 处理。

有没有一个相当成熟的Java JSON/REST客户端API以事件驱动的方式使用响应?

我想它会让你做这样的事情:

 RestStreamer rest = new RestStreamer(clientHttpRequestFactory);

 // Tell the RestStreamer "when, while parsing a response, you encounter a JSON
 // element matching JSONPath "$.items[*]" pass it to "handler" for processing.
 rest.onJsonPath("$.items[*]").handle(handler);

 // Tell the RestStreamer to make an HTTP request, parse it as a stream.
 // We expect "handler" to get passed an object each time the parser encounters
 // an item.
 rest.execute(url, HttpMethod.GET, requestEntity);

我很感激我可以使用Jackson,GSON等的流式JSON API来滚动我自己实现这种行为 - 但我很想被告知,有一些东西可以通过简洁,富有表现力的API可靠地做到这一点,并与HTTP方面集成。


答案 1

几个月后;回过头来回答我自己的问题。

我没有找到一个富有表现力的API来做我想做的事情,但是我能够通过将HTTP主体作为流来实现所需的行为,并用Jackson使用它:JsonParser

  ClientHttpRequest request = 
        clientHttpRequestFactory.createRequest(uri, HttpMethod.GET);
  ClientHttpResponse response = request.execute();

  return handleJsonStream(response.getBody(), handler);

...使用handleJsonStream设计用于处理看起来像这样的JSON:

 { items: [ 
      { field: value; ... }, 
      { field: value, ... },
      ... thousands more ... 
 ] }

...它验证导致数组开始的令牌;它每次遇到数组元素时都会创建一个对象,并将其提供给处理程序。Item

 // important that the JsonFactory comes from an ObjectMapper, or it won't be
 // able to do readValueAs()
 static JsonFactory jsonFactory = new ObjectMapper().getFactory();

 public static int handleJsonStream(InputStream stream, ItemHandler handler) throws IOException {

     JsonParser parser = jsonFactory.createJsonParser(stream);

     verify(parser.nextToken(), START_OBJECT, parser);
     verify(parser.nextToken(), FIELD_NAME, parser);
     verify(parser.getCurrentName(), "items", parser);
     verify(parser.nextToken(), START_ARRAY, parser);
     int count = 0;
     while(parser.nextToken() != END_ARRAY) {
        verify(parser.getCurrentToken(), START_OBJECT, parser);
        Item item = parser.readValueAs(Item.class);
        handler.onItem(item);
        count++;
     }
     parser.close(); // hope it's OK to ignore remaining closing tokens.
     return count;
 }

verify()只是一个私有静态方法,如果前两个参数不相等,则会引发异常。

此方法的关键是,无论流中有多少个项目,此方法都只有每个项目都有对一个 Item 的引用。


答案 2

你可以尝试JsonSurfer,它旨在以事件驱动的风格处理json流。

JsonSurfer surfer = JsonSurfer.jackson();
Builder builder = config();
builder.bind("$.items[*]", new JsonPathListener() {
        @Override
        public void onValue(Object value, ParsingContext context) throws Exception {
            // handle the value
        }
    });
surfer.surf(new InputStreamReader(response.getBody()), builder.build());