diff --git a/release-notes/CREDITS b/release-notes/CREDITS index 3234c19722..5ef9a51153 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -40,3 +40,7 @@ Andy Boothe (@sigpwned) * Contributed #5025: Add support for automatic detection of subtypes (like `@JsonSubTypes`) from Java 17 sealed types [3.0.0] + +Sébastien Deleuze (@sdeleuze) + * Requested #5110: Add missing `ObjectMapper#createNonBlockingByteBufferParser()` method + [3.0.0] diff --git a/release-notes/VERSION b/release-notes/VERSION index 06c66d7708..175fabe458 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -11,6 +11,8 @@ Versions: 3.x (for earlier see VERSION-2.x) to use `Supplier` #5094: Change the way `BeanDescription` passed during deserializer construction to use `Supplier` +#5110: Add missing `ObjectMapper#createNonBlockingByteBufferParser()` method + (requested by Sébastien D) 3.0.0-rc3 (13-Apr-2025) diff --git a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java index 0d161bc952..49b4a701f4 100644 --- a/src/main/java/tools/jackson/databind/ObjectMapper.java +++ b/src/main/java/tools/jackson/databind/ObjectMapper.java @@ -737,7 +737,7 @@ public JsonParser createParser(DataInput content) throws JacksonException { * Factory method for constructing non-blocking {@link JsonParser} that is properly * wired to allow configuration access (and, if relevant for parser, callbacks): * essentially constructs a {@link ObjectReadContext} and then calls - * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. + * {@link TokenStreamFactory#createNonBlockingByteArrayParser(ObjectReadContext)}. * * @since 3.0 */ @@ -746,6 +746,19 @@ public JsonParser createNonBlockingByteArrayParser() throws JacksonException { return ctxt.assignAndReturnParser(_streamFactory.createNonBlockingByteArrayParser(ctxt)); } + /** + * Factory method for constructing non-blocking {@link JsonParser} that is properly + * wired to allow configuration access (and, if relevant for parser, callbacks): + * essentially constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createNonBlockingByteBufferParser(ObjectReadContext)}. + * + * @since 3.0 + */ + public JsonParser createNonBlockingByteBufferParser() throws JacksonException { + DeserializationContextExt ctxt = _deserializationContext(); + return ctxt.assignAndReturnParser(_streamFactory.createNonBlockingByteBufferParser(ctxt)); + } + /* /********************************************************************** /* Public API: constructing Generator that are properly linked diff --git a/src/main/java/tools/jackson/databind/ObjectReader.java b/src/main/java/tools/jackson/databind/ObjectReader.java index 6419b2e0d9..feb6bcc720 100644 --- a/src/main/java/tools/jackson/databind/ObjectReader.java +++ b/src/main/java/tools/jackson/databind/ObjectReader.java @@ -961,7 +961,7 @@ public JsonParser createParser(DataInput content) throws JacksonException { * Factory method for constructing non-blocking {@link JsonParser} that is properly * wired to allow configuration access (and, if relevant for parser, callbacks): * essentially constructs a {@link ObjectReadContext} and then calls - * {@link TokenStreamFactory#createParser(ObjectReadContext,DataInput)}. + * {@link TokenStreamFactory#createNonBlockingByteArrayParser(ObjectReadContext)}. * * @since 3.0 */ @@ -970,6 +970,19 @@ public JsonParser createNonBlockingByteArrayParser() throws JacksonException { return ctxt.assignAndReturnParser(_parserFactory.createNonBlockingByteArrayParser(ctxt)); } + /** + * Factory method for constructing non-blocking {@link JsonParser} that is properly + * wired to allow configuration access (and, if relevant for parser, callbacks): + * essentially constructs a {@link ObjectReadContext} and then calls + * {@link TokenStreamFactory#createNonBlockingByteBufferParser(ObjectReadContext)}. + * + * @since 3.0 + */ + public JsonParser createNonBlockingByteBufferParser() throws JacksonException { + DeserializationContextExt ctxt = _deserializationContext(); + return ctxt.assignAndReturnParser(_parserFactory.createNonBlockingByteBufferParser(ctxt)); + } + /* /********************************************************************** /* TreeCodec implementation diff --git a/src/test/java/tools/jackson/databind/deser/MergePolymorphicTest.java b/src/test/java/tools/jackson/databind/deser/MergePolymorphicTest.java index 9fe0a104d1..b6430e33ac 100644 --- a/src/test/java/tools/jackson/databind/deser/MergePolymorphicTest.java +++ b/src/test/java/tools/jackson/databind/deser/MergePolymorphicTest.java @@ -15,7 +15,6 @@ public class MergePolymorphicTest { - static class Root { @JsonMerge public Child child; diff --git a/src/test/java/tools/jackson/databind/deser/NonBlockingDeserTest.java b/src/test/java/tools/jackson/databind/deser/NonBlockingDeserTest.java index 095ac2276a..b88317a04a 100644 --- a/src/test/java/tools/jackson/databind/deser/NonBlockingDeserTest.java +++ b/src/test/java/tools/jackson/databind/deser/NonBlockingDeserTest.java @@ -6,17 +6,19 @@ import tools.jackson.core.JsonParser; import tools.jackson.core.ObjectReadContext; +import tools.jackson.core.async.ByteArrayFeeder; import tools.jackson.core.async.ByteBufferFeeder; import tools.jackson.databind.ObjectMapper; import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.testutil.DatabindTestUtil; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests for checking that test deserialization with non-blocking parsers */ -public class NonBlockingDeserTest +public class NonBlockingDeserTest extends DatabindTestUtil { record Foo(String bar) {} @@ -26,24 +28,66 @@ record Foo(String bar) {} /********************************************************** */ + private final static int TEST_ITEM_COUNT = 10; + + private final ObjectMapper MAPPER = newJsonMapper(); + + private final byte[] TEST_DOC = _testDoc(TEST_ITEM_COUNT); + @Test - public void testNonBlockingParser() + public void testNonBlockingByteArrayParserViaMapper() { - final ObjectMapper m = JsonMapper.builder() - //.disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) - .build(); - final int len = 10; - Foo[] foos = new Foo[len]; - for (int i = 0; i < len; ++i) { - foos[i] = new Foo("bar-" + i); + try (final JsonParser parser = + MAPPER.createNonBlockingByteArrayParser()) { + ((ByteArrayFeeder) parser).feedInput(TEST_DOC, 0, TEST_DOC.length); + ((ByteArrayFeeder) parser).endOfInput(); + Foo[] result = MAPPER.readValue(parser, Foo[].class); + assertEquals(TEST_ITEM_COUNT, result.length); } - final Foo[] result; + } + + @Test + public void testNonBlockingByteArrayParserViaReader() + { try (final JsonParser parser = - m.tokenStreamFactory().createNonBlockingByteBufferParser(ObjectReadContext.empty())) { - ((ByteBufferFeeder) parser).feedInput(ByteBuffer.wrap(m.writeValueAsBytes(foos))); + MAPPER.reader().createNonBlockingByteArrayParser()) { + ((ByteArrayFeeder) parser).feedInput(TEST_DOC, 0, TEST_DOC.length); + ((ByteArrayFeeder) parser).endOfInput(); + Foo[] result = MAPPER.readValue(parser, Foo[].class); + assertEquals(TEST_ITEM_COUNT, result.length); + } + } + + @Test + public void testNonBlockingByteBufferParserViaMapper() + { + try (final JsonParser parser = + MAPPER.createNonBlockingByteBufferParser()) { + ((ByteBufferFeeder) parser).feedInput(ByteBuffer.wrap(TEST_DOC)); + ((ByteBufferFeeder) parser).endOfInput(); + Foo[] result = MAPPER.readValue(parser, Foo[].class); + assertEquals(TEST_ITEM_COUNT, result.length); + } + } + + @Test + public void testNonBlockingByteBufferParserViaReader() + { + try (final JsonParser parser = + MAPPER.reader().createNonBlockingByteBufferParser()) { + ((ByteBufferFeeder) parser).feedInput(ByteBuffer.wrap(TEST_DOC)); ((ByteBufferFeeder) parser).endOfInput(); - result = m.readValue(parser, Foo[].class); + Foo[] result = MAPPER.readValue(parser, Foo[].class); + assertEquals(TEST_ITEM_COUNT, result.length); + } + } + + private byte[] _testDoc(int count) { + Foo[] foos = new Foo[count]; + for (int i = 0; i < count; ++i) { + foos[i] = new Foo("bar-" + i); } - assertEquals(len, result.length); + return MAPPER.writeValueAsBytes(foos); } + }