Repository navigation
Conversation
Add ForyJson.readUtf8(bytes, offset, length, operation) and ForyJson.writeUtf8(operation), which lend a pooled Utf8JsonReader or Utf8JsonWriter to caller code. This lets frameworks that bring their own (de)serializers, for example ones generated at compile time, use Fory JSON's reader and writer through a supported API instead of reaching package-private JsonConfig and JsonTypeResolver. The operations reuse the existing fixed pool, so reader field-name caches stay bounded, follow the same lockJIT/cleanup discipline as the other root APIs, reject trailing content after reads, and propagate the operation's checked exceptions unchanged. Existing ForyJson code paths are not modified.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why?
Micronaut is a JVM framework built around a compile-time-first approach: dependency injection, AOP, bean introspection and configuration are resolved by annotation processors at build time instead of with runtime reflection, proxies or bytecode generation. This keeps startup fast and memory low, and makes applications work in GraalVM native images without reflection configuration.
Micronaut Serialization applies the same idea to JSON. (De)serializers are generated at compile time, and a small streaming backend does the tokenizing and writing; today that backend is usually jackson-core.
Fory JSON's
Utf8JsonReader/Utf8JsonWriterare a very good fit for that backend role. We built a Micronaut Serialization backend on top of them that keeps Micronaut's generated (de)serializers and uses none of Fory's codecs. On Micronaut's user-bean benchmark it is clearly faster than the same generated code on jackson-core; numbers are below. It passes Micronaut Serialization's full TCK on the JVM and in a GraalVM native image.The obstacle is that there is no supported way to obtain a reader or writer outside
ForyJson's object mapping. Creating one needs aJsonConfig(no public constructor;ForyJson.config()is package-private) and aJsonTypeResolver. Our prototype had to put a helper class into theorg.apache.fory.jsonpackage (a split package that breaks on the module path) and create its own reader instances outsideForyJson's fixed pool. As the comment in theForyJsonconstructor explains, that pool is intentionally the only owner of execution state, so that field-name caches stay bounded.This PR adds the smallest supported entry point for that use case and changes nothing else. It is a first step for #4164. A standalone reader/writer artifact can be discussed separately there.
What does this PR do?
ForyJson.readUtf8(byte[] bytes, int offset, int length, Utf8ReadOperation<R, E> operation):Utf8JsonReaderfor the byte range to the operation;ForyJson.writeUtf8(Utf8WriteOperation<E> operation), which lends the pooledUtf8JsonWriterand returns the written bytes as a detached array.Utf8ReadOperation<R, E extends Throwable>andUtf8WriteOperation<E extends Throwable>. Checked exceptions thrown by the caller's (de)serializers, such asIOException, propagate unchanged without wrapping.acquire()/release()of the fixed pool,lockJIT()/unlockJIT(), and the same buffer handoff and cleanup asfromJson(byte[], int, int, …)andtoJsonBytes(…). Field-name caches therefore stay bounded, and the readers and writers never escape the pool.ForyJsonTokenOperationTest:docs/json/integration.md. It includes a note that external readers should callenterDepth/exitDepthandreserveGraphMemory, as custom codecs do, for the depth and graph-memory limits to apply.No existing code path is modified, and no existing public API changes.
Validation from the consumer side: we rebased the Micronaut Serialization backend onto exactly this API, using a local
1.8.0-SNAPSHOTbuild of this branch. It no longer needs the split-package helper or its own reader/writer pool. Micronaut's Fory module tests (1226) and the serialization TCK suite pass unchanged on top ofreadUtf8/writeUtf8.Related issues
AI Contribution Checklist
yesyes, I included a completed AI Contribution Checklist in this PR description and the requiredAI Usage Disclosure.yes, my PR description includes the requiredai_reviewsummary and evidence of the final clean AI review results from both fresh reviewers described inAI_POLICY.md, on the current HEAD.Does this PR introduce any user-facing change?
ForyJson.readUtf8,ForyJson.writeUtf8,Utf8ReadOperation,Utf8WriteOperation)Benchmark
This PR does not modify existing code paths, so existing
ForyJsonperformance is unaffected.For context, these are consumer-side results from Micronaut Serialization's
UserBeanSerdeBenchmark-style model (users with nested friends, tags and scalars). They compare the same compile-time-generated Micronaut (de)serializers running on jackson-core and on Fory'sUtf8JsonReader/Utf8JsonWriter(JDK 25, 2 forks, 3×2s warmup, 5×2s measurement):