How do you set an ObjectCodec on a JsonParser when

2020-03-27 05:09发布

Note: this is using Jackson 2.3.2

For the needs of one of my projects, I am writing a custom JsonParser which records a Map where keys are JsonPointers and values are Integers (line numbers where the pointer points to).

The class is named LineRecorderJsonParser. It is produced by a LineRecorderJsonFactory which is a simple delegate:

public final class LineRecorderJsonFactory
    extends JsonFactory
{
    @Override
    protected JsonParser _createParser(final InputStream in,
        final IOContext ctxt)
        throws IOException, JsonParseException
    {
        final JsonParser parser = super._createParser(in, ctxt);
        return new LineRecorderJsonParser(parser);
    }

    @Override
    protected JsonParser _createParser(final Reader r, final IOContext ctxt)
        throws IOException, JsonParseException
    {
        final JsonParser parser = super._createParser(r, ctxt);
        return new LineRecorderJsonParser(parser);
    }

    @Override
    protected JsonParser _createParser(final byte[] data, final int offset,
        final int len, final IOContext ctxt)
        throws IOException, JsonParseException
    {
        final JsonParser parser = super._createParser(data, offset, len, ctxt);
        return new LineRecorderJsonParser(parser);
    }
}

I have tested that the code works using the following code:

public static void main(final String... args)
    throws IOException
{
    final JsonFactory factory = new LineRecorderJsonFactory();
    final ObjectMapper mapper = new ObjectMapper(factory);
    final Closer closer = Closer.create();
    final InputStream in;

    try {
        in = closer.register(LineTesting.class.getResourceAsStream
            ("/testfile.json"));
        mapper.readTree(in);
    } finally {
        closer.close();
    }
}

But now I want to test it; so I wrote the following test code:

public final class LineRecorderJsonParserTest
{
    private static final String INCORRECT_LINE_INFO
        = "generated line info is incorrect; expected: %s, actual: %s";

    private JsonFactory factory;
    private ObjectMapper mapper;

    @BeforeMethod
    public void initFactory()
    {
        factory = new LineRecorderJsonFactory();
        mapper = new ObjectMapper();
    }

    @DataProvider
    public Iterator<Object[]> getLineData()
    {
        final List<Object[]> list = Lists.newArrayList();

        list.add(new Object[] { "1" });
        return list.iterator();
    }

    @Test(dataProvider = "getLineData")
    public void lineNumbersAreCorrectlyReported(final String subdir)
        throws IOException
    {
        final String basePath = "/parser/" + subdir + '/';
        final Closer closer = Closer.create();
        final TypeReference<Map<JsonPointer, Integer>> typeRef
            = new TypeReference<Map<JsonPointer, Integer>>() {};

        final InputStream input, lines;
        final Map<JsonPointer, Integer> actual, expected;
        final LineRecorderJsonParser parser;

        try {
            input = closer.register(inputFrom(basePath + "input.json"));
            lines = closer.register(inputFrom(basePath + "lines.json"));

            expected = mapper.readValue(lines, typeRef);

            parser = (LineRecorderJsonParser) factory.createParser(input);
            //parser.setCodec(factory.getCodec()); // Doesn't change anything...
            parser.readValueAsTree(); // FAILS HERE
            actual = parser.getLineInfo();

            assertEquals(actual, expected,
                String.format(INCORRECT_LINE_INFO, expected, actual));
        } catch (IOException e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    private static InputStream inputFrom(final String path)
    {
        return LineRecorderJsonParserTest.class.getResourceAsStream(path);
    }
}

Unfortunately this doesn't work. I get the following exception:

java.lang.IllegalStateException: No ObjectCodec defined for the parser, can not deserialize JSON into JsonNode tree
    at com.fasterxml.jackson.core.JsonParser.readValueAsTree(JsonParser.java:1507)
    at com.fasterxml.jackson.core.util.JsonParserDelegate.readValueAsTree(JsonParserDelegate.java:371)
    at com.github.fge.jackson.parse.LineRecorderJsonParserTest.lineNumbersAreCorrectlyReported(LineRecorderJsonParserTest.java:84)

Even when I (try and) set an ObjectCodec myself (see code), I get this exception...

What am I doing wrong? How can I test this class, since when you read using an ObjectMapper directly, you don't get a chance to grab the underlying parser?

标签: java jackson
2条回答
兄弟一词,经得起流年.
2楼-- · 2020-03-27 05:48

Or, JsonFactory.setCodec(mapper).

查看更多
▲ chillily
3楼-- · 2020-03-27 06:01

Well, I found out how to do that not even minutes after I asked the question... I'll answer to self should it prove useful for other people.

First of all, my JsonFactory's .getCodec() returned null; and JsonParser doesn't yell at you if you try and feed it a null codec.

Second, ObjectMapper extends ObjectCodec, and ObjectCodec has a method to read a tree (it has, in fact, all methods you customarily use on an ObjectMapper).

Therefore, changing the code to this worked:

parser = (LineRecorderJsonParser) factory.createParser(input);
mapper.readTree(parser);
actual = parser.getLineInfo();
查看更多
登录 后发表回答