Java 如何使用Jackson正确解析流式JSON?

Java 如何使用Jackson正确解析流式JSON?,java,json,jackson,streaming,Java,Json,Jackson,Streaming,我正试图找到一种干净的方法,用Jackson解析流式JSON。“流式传输”与TCP一样,以零碎的方式进行,不保证在一次读取中接收完整的JSON数据(也没有消息帧)。另外,我们的目标是异步完成这项工作,这就排除了依赖Jackson对java.io.InputStreams的处理。我提出了一个有效的解决方案(见下面的演示),但我对它不是特别满意。撇开命令式风格不谈,我不喜欢JsonParser#readValueAsTree对不完整JSON的笨拙处理。在处理字节流时,不完整的数据是绝对正常的,并且不

我正试图找到一种干净的方法,用Jackson解析流式JSON。“流式传输”与TCP一样,以零碎的方式进行,不保证在一次读取中接收完整的JSON数据(也没有消息帧)。另外,我们的目标是异步完成这项工作,这就排除了依赖Jackson对
java.io.InputStream
s的处理。我提出了一个有效的解决方案(见下面的演示),但我对它不是特别满意。撇开命令式风格不谈,我不喜欢
JsonParser#readValueAsTree
对不完整JSON的笨拙处理。在处理字节流时,不完整的数据是绝对正常的,并且不是异常情况,因此在Jackson的API中看到
java.io.IOException
s是奇怪的(也是不可接受的)。我还研究了使用Jackson的
令牌缓冲区
,但遇到了类似的问题。Jackson真的不适合处理真正的流式JSON吗

package com.example.jackson;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyList;

public class AsyncJsonParsing {
    public static void main(String[] args) {
        final AsyncJsonParsing parsing = new AsyncJsonParsing();

        parsing.runFirstScenario();
        parsing.runSecondScenario();
        parsing.runThirdScenario();
        parsing.runFourthScenario();
    }



    static final class ParsingOutcome {
        final List<JsonNode> roots;//list of parsed JSON objects and JSON arrays
        final byte[] remainder;

        ParsingOutcome(final List<JsonNode> roots, final byte[] remainder) {
            this.roots = roots;
            this.remainder = remainder;
        }
    }

    final byte[] firstMessage = "{\"message\":\"first\"}".getBytes(UTF_8);
    final byte[] secondMessage = "{\"message\":\"second\"}".getBytes(UTF_8);

    final byte[] leadingHalfOfFirstMessage = Arrays.copyOfRange(firstMessage, 0, firstMessage.length / 2);
    final byte[] trailingHalfOfFirstMessage = Arrays.copyOfRange(firstMessage, firstMessage.length / 2, firstMessage.length);

    final byte[] leadingHalfOfSecondMessage = Arrays.copyOfRange(secondMessage, 0, secondMessage.length / 2);
    final byte[] trailingHalfOfSecondMessage = Arrays.copyOfRange(secondMessage, secondMessage.length / 2, secondMessage.length);

    final ObjectMapper mapper = new ObjectMapper();

    void runFirstScenario() {
        //expectation: remainder = empty array and roots has a single element - parsed firstMessage
        final ParsingOutcome result = parse(firstMessage, mapper);
        report(result);
    }

    void runSecondScenario() {
        //expectation: remainder = leadingHalfOfFirstMessage and roots is empty
        final ParsingOutcome firstResult = parse(leadingHalfOfFirstMessage, mapper);
        report(firstResult);

        //expectation: remainder = empty array and roots has a single element - parsed firstMessage
        final ParsingOutcome secondResult = parse(concat(firstResult.remainder, trailingHalfOfFirstMessage), mapper);
        report(secondResult);
    }

    void runThirdScenario() {
        //expectation: remainder = leadingHalfOfSecondMessage and roots has a single element - parsed firstMessage
        final ParsingOutcome firstResult = parse(concat(firstMessage, leadingHalfOfSecondMessage), mapper);
        report(firstResult);

        //expectation: remainder = empty array and roots has a single element - parsed secondMessage
        final ParsingOutcome secondResult = parse(concat(firstResult.remainder, trailingHalfOfSecondMessage), mapper);
        report(secondResult);
    }

    void runFourthScenario() {
        //expectation: remainder = empty array and roots has two elements - parsed firstMessage, followed by parsed secondMessage
        final ParsingOutcome result = parse(concat(firstMessage, secondMessage), mapper);
        report(result);
    }

    static void report(final ParsingOutcome result) {
        System.out.printf("Remainder of length %d: %s%n", result.remainder.length, Arrays.toString(result.remainder));
        System.out.printf("Total of %d parsed JSON roots: %s%n", result.roots.size(), result.roots);
    }

    static byte[] concat(final byte[] left, final byte[] right) {
        final byte[] union = Arrays.copyOf(left, left.length + right.length);
        System.arraycopy(right, 0, union, left.length, right.length);
        return union;
    }

    static ParsingOutcome parse(final byte[] chunk, final ObjectMapper mapper) {
        final List<JsonNode> roots = new LinkedList<>();

        JsonParser parser;
        JsonNode root;
        try {
            parser = mapper.getFactory().createParser(chunk);
            root = parser.readValueAsTree();
        } catch (IOException e) {
            return new ParsingOutcome(emptyList(), chunk);
        }

        byte[] remainder = new byte[0];
        try {
            while(root != null) {
                roots.add(root);
                remainder = extractRemainder(parser);
                root = parser.readValueAsTree();
            }
        } catch (IOException e) {
            //fallthrough
        }

        return new ParsingOutcome(roots, remainder);
    }

    static byte[] extractRemainder(final JsonParser parser) {
        try {
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            parser.releaseBuffered(baos);
            return baos.toByteArray();
        } catch (IOException e) {
            return new byte[0];
        }
    }
}
package com.example.jackson;
导入com.fasterxml.jackson.core.JsonParser;
导入com.fasterxml.jackson.databind.JsonNode;
导入com.fasterxml.jackson.databind.ObjectMapper;
导入java.io.ByteArrayOutputStream;
导入java.io.IOException;
导入java.util.array;
导入java.util.LinkedList;
导入java.util.List;
导入静态java.nio.charset.StandardCharsets.UTF_8;
导入静态java.util.Collections.emptyList;
公共类AsyncJsonParsing{
公共静态void main(字符串[]args){
final AsyncJsonParsing=new AsyncJsonParsing();
正在解析;
正在解析;
正在解析.runThirdScenario();
解析.runFourthScenario();
}
静态最终类ParsingOutcome{
最终列表根;//已解析JSON对象和JSON数组的列表
最后字节[]余数;
ParsingOutcome(最终列表根,最终字节[]余数){
这个。根=根;
这个。余数=余数;
}
}
最后一个字节[]firstMessage=“{\“message\”:\“first\”}.getBytes(UTF_8);
最后一个字节[]secondMessage=“{\“message\”:\“second\”}.getBytes(UTF_8);
最后一个字节[]leadinghalOffirstMessage=Arrays.copyOfRange(firstMessage,0,firstMessage.length/2);
最后一个字节[]trailingHalfOfFirstMessage=Arrays.copyOfRange(firstMessage,firstMessage.length/2,firstMessage.length);
最后一个字节[]leadingHalfOfSecondMessage=Arrays.copyOfRange(secondMessage,0,secondMessage.length/2);
最后一个字节[]trailinghalfSecondMessage=Arrays.copyOfRange(secondMessage,secondMessage.length/2,secondMessage.length);
最终ObjectMapper映射器=新ObjectMapper();
void runFirstScenario(){
//期望值:rements=空数组,并且根具有单个元素-已解析的firstMessage
最终ParsingOutcome result=parse(第一条消息,映射器);
报告(结果);
}
void runSecondScenario(){
//期望值:余数=leadinghalOffirstMessage,根为空
最终ParsingOutcome firstResult=parse(leadingoffirstmessage,mapper);
报告(第一次结果);
//期望值:rements=空数组,并且根具有单个元素-已解析的firstMessage
最终ParsingOutcome secondResult=parse(concat(firstResult.rements,trailingHalfOfFirstMessage),映射器);
报告(第二次结果);
}
void runThirdScenario(){
//expection:rements=leadingHalfOfSecondMessage,根具有单个元素-解析的firstMessage
最终ParsingOutcome firstResult=parse(concat(firstMessage,leadingHalfOfSecondMessage),映射器);
报告(第一次结果);
//期望值:rements=空数组,根具有单个元素-已解析的secondMessage
最终ParsingOutcome secondResult=parse(concat(firstResult.rements,trailinghalof secondmessage),映射器);
报告(第二次结果);
}
void runFourthScenario(){
//期望值:rements=空数组,根有两个元素—解析的firstMessage,后跟解析的secondMessage
最终ParsingOutcome结果=parse(concat(firstMessage,secondMessage),mapper);
报告(结果);
}
静态无效报告(最终分析结果){
System.out.printf(“长度%d的剩余部分:%s%n”,result.rements.length,Arrays.toString(result.rements));
System.out.printf(“解析的JSON根的总数为%d个:%s%n”,result.root.size(),result.root);
}
静态字节[]concat(最后一个字节[]左,最后一个字节[]右){
最后一个字节[]union=Arrays.copyOf(left,left.length+right.length);
System.arraycopy(右,0,并集,左.length,右.length);
回归联盟;
}
静态ParsingOutcome解析(最终字节[]块,最终对象映射器映射器){
最终列表根=新的LinkedList();
JsonParser解析器;
JsonNode根;
试一试{
parser=mapper.getFactory().createParser(块);
root=parser.readValueAsTree();
}捕获(IOE异常){
返回新的ParsingOutcome(emptyList(),chunk);
}
字节[]余数=新字节[0];
试一试{
while(root!=null){
根。添加(根);
余数=提取余数(解析器);
root=parser.readValueAsTree();
}
}捕获(IOE异常){
//失败
}
返回新的ParsingOutcome(根,余数);
}
静态字节[]提取余数(最终JsonParser){
试一试{
最终ByteArrayOutputStream bas=新ByteArrayOutputStream();
releaseBuffered解析器(baos);
返回baos.toByteArray();
}捕获(IOE异常){
返回新字节[0];
}
}
}
再进一步阐述一下,在概念上(至少在我看来),任何流数据的解析都可以归结为一个简单的函数,它接受一个byt数组