是否有针对Java的事件驱动的JSON REST客户端API?

sli*_*lim 24 java rest json

我有一个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);
 }
Run Code Online (Sandbox Code Playgroud)

JSON响应包含一个项目列表,正如您所看到的,我在自己的代码中有一个事件驱动的设计来依次处理每个项目.然而,整个列表是在存储器中作为其一部分response,其RestTemplate.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);
Run Code Online (Sandbox Code Playgroud)

我很欣赏我可以使用来自Jackson,GSON等的流式JSON API来实现这种行为的实现 - 但是我很想知道那里有一些东西能够可靠地使用简洁,富有表现力的API,与HTTP方面.

sli*_*lim 6

几个月后; 回来回答我自己的问题.

我没有找到一个表达性的API来做我想要的,但我能够通过将HTTP主体作为流来实现所需的行为,并使用Jackson来消费它JsonParser:

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

  return handleJsonStream(response.getBody(), handler);
Run Code Online (Sandbox Code Playgroud)

...使用handleJsonStream设计来处理如下所示的JSON:

 { items: [ 
      { field: value; ... }, 
      { field: value, ... },
      ... thousands more ... 
 ] }
Run Code Online (Sandbox Code Playgroud)

...它验证了导致阵列开始的令牌; 它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;
 }
Run Code Online (Sandbox Code Playgroud)

verify() 只是一个私有静态方法,如果前两个参数不相等则抛出异常.

关于这个方法的关键是无论流中有多少项,这个方法只有每个都有一个Item的引用.


小智 4

你可以尝试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());
Run Code Online (Sandbox Code Playgroud)