Releases: jdereg/json-io
Releases · jdereg/json-io
4.101.0
json-io 4.101.0
Maven Central: com.cedarsoftware:json-io:4.101.0 (requires java-util 4.101.0+)
Highlights
New APIs
WriteOptionsBuilder.standardJson()— one-call convenience settingshowTypeInfoNever(),showRootTypeInfo(false),cycleSupport(false),stringifyMapKeys(true),useMetaPrefixDollar(), and ISO-8601 date formatting. Produces Jackson-compatible JSON for normal POJO/List/Map cases. Chainable. Will become the default in 5.0.0.WriteOptionsBuilder.namingStrategy(IoNaming.Strategy)— global naming strategy applied to all classes without a per-class@IoNaming/@JsonNaming. Priority: per-field@IoProperty/@JsonProperty> per-class annotation > global strategy > field name as-is. Two new enum values:UPPER_SNAKE_CASEandLOWER_CASE(full set:SNAKE_CASE,UPPER_SNAKE_CASE,KEBAB_CASE,UPPER_CAMEL_CASE,LOWER_DOT_CASE,LOWER_CASE).WriteOptionsBuilder.stringifyMapKeys(boolean)— non-String map keys with bidirectional String conversion (Long,UUID,Enum,Date, etc.) written as plain JSON object keys instead of@keys/@itemsparallel-array form. Defaultfalsefor JSON (backward-compat),truefor JSON5.WriteOptionsBuilder.preserveLeafContainerIdentity(boolean)— whenfalse(the new default),traceReferencesskips containers holding only non-referenceable leaves (List<String>,Map<UUID, Date>,String[], etc.), matching Jackson's default identity semantics.WriteOptionsBuilder.writeOptionalAsObject(boolean)— set totruefor legacy{"present":true,"value":X}form; defaultfalsewrites Optionals as bare primitive values.- 1-arg convenience overloads:
JsonIo.toJson(obj),toToon(obj),toJava(json),toJava(stream),fromToon(toon),fromToon(stream).
Performance
- Full-suite performance pass — all 8 Read/Write ratios under 2.0x vs Jackson on a warm JVM (best 1.32x JsonIo Read toMaps, worst ~1.97x Toon Read toJava).
- Switched all hot-path
ClassValueMap/ClassValueSetlookups to the newgetByClass(Class)/containsClass(Class)fast-path APIs (added in java-util 4.101.0). AnnotationResolver.getMetadata— split into trivially-inlineable fast path +scanAndCachecold path. Fast-path self-time dropped ~50%.ObjectResolver.traverseFields— hoisted per-objectAnnotationResolver.getMetadatacall out of the per-field loop (~20% of Resolver-phase samples).ReadOptions.isNonReferenceableClass/isNotCustomReaderClass— memoized via per-instanceClassValue<Boolean>caches (mirrorsWriteOptions.nonRefCache).JsonWriter.traceReferences— filters non-referenceable leaves at push site (save deque push+pop per leaf). JsonIo Write cycle=true toJava −5.8%, toMaps −7.5%.ToonWriter— precomputed key-quoting decision atWriteFieldPlanbuild time (eliminates per-fieldConcurrentHashMap.get— 11.7% of ToonWriter samples). Toon Write cycle=false toJava −5.0%.JsonWriter.writeField/ToonWriter.writeFieldEntry— primitive+String fast paths bypass full dispatch for the most common field-value types.ToonWriter.writeMap— skipshasComplexKeysiteration when the enclosing field's declared Map key type is Converter-stringifiable.JsonWriter.writeCollectionElement— POJO short-circuit forcycleSupport=falsecollapses 4-level dispatch to 2 levels.
Bug fixes
Optional,OptionalInt,OptionalLong,OptionalDouble— all four now write Jackson/Gson-compatible primitive form by default (Optional.empty()→null,Optional.of(X)→X). Prior behavior emitted invalid JSON forOptional({null}or{"hello"}) and broken POJO form for the three primitive Optionals. Field-type-aware coercion wraps bare scalars into the appropriate Optional variant on read. Reader accepts both new and legacy object form for backward compatibility. Reported by Jim Ronan (*/dxg Health).JsonWriter.writeLongDirect()— inner digit-extraction loop usedint qwhich silently truncatedLong.MIN_VALUE(rendered as-206158430208instead of-9223372036854775808). Fixed by usinglong q.
Jackson-alignment (opt-in)
WriteOptionsBuilder.standardJson()andjson5()now also configure ISO-8601 date formatting forjava.util.Date/java.sql.Date. Matches Spring Boot's default Jackson configuration (WRITE_DATES_AS_TIMESTAMPS=false).
Full changelog: https://github.com/jdereg/json-io/blob/master/changelog.md
4.100.0
4.100.0 - 2026-04-10
Bug Fixes
- Fixed deadlock from circular static initialization between
ReadOptionsBuilderandWriteOptionsBuilder. When two threads concurrently triggered class loading (e.g., one callingJsonIo.toJson()and another callingJsonIo.toJava()), the JVM's class initialization locks would deadlock. Broke the cycle by having each builder load its configuration independently and by removing the circular dependency throughMetaUtilsbootstrap methods.
Dependency Updates
- java-util 4.100.0 (includes fix for unbounded
Converter.FULL_CONVERSION_CACHEmemory leak) - JUnit 5.14.2 → 5.14.3
- Jackson 2.21.1 → 2.21.2
4.99.0
4.99.0 - 2026-03-28
- PERFORMANCE:
ToonWriter.needsQuoting()— replaced multi-method dispatch chain with a single-passscanNeedsQuoting()method. Uses a pre-builtboolean[128]lookup table per delimiter. JFR showsneedsQuotingdropped from 383 samples to 25 — a 93.5% reduction. - PERFORMANCE:
ToonReader.readLineRaw()— now usesFastReader.readLine()instead ofreadUntil()+ separateread()+ pushback. JFR shows TOON line-reading improved 28%. - PERFORMANCE:
JsonWriter.writePrimitive()— added small-integer String cache (-128 to 16384) matching ToonWriter's range. JFR shows 898 allocation samples eliminated. - PERFORMANCE:
ToonWriter— cycle-tracking structures allocated lazily based oncycleSupportmode. WhencycleSupport=false(default), saves ~6 KB per writer. TOON write time improved 4-7%, reaching 2.02x Jackson. - PERFORMANCE:
ToonReader.findColonInBuf()— single-pass scan withc <= ':'range guard. JFR shows 8-11% improvement in TOON read time. - PERFORMANCE:
ToonReaderstring/number caching — replaced O(n) polynomial hash loops with O(1)cacheHash()sampling first, middle, and last chars + length. JFR shows 36% reduction. TOON read time improved 9-14%, reaching parity with json-io's JSON read. - PERFORMANCE:
JsonWriter.writeObjectArray()now fast-pathsBoolean,Double,Long,Integer,Float,Short,Byte, andStringelements directly towritePrimitive()/writeStringValue(), bypassing thewriteImpl()dispatch chain. - CLEANUP: Removed unused
hasSameKeyOrder()method fromToonWriter. - CLEANUP: Removed two unreachable dead branches from
writeFieldEntry()andwriteFieldEntryInline(). - CLEANUP: Fixed Javadoc tag error in
getObjectFields().
4.98.0
Highlights
@IoShowTypeannotation — new field-level annotation (26th) that forces@type/$typeemission on polymorphic fields regardless ofshowTypeInfomode. Jackson's@JsonTypeInfoon a field is honored as a fallback synonym.- JSON5 defaults alignment —
.json5()now setsshowTypeInfoNever()andcycleSupport(false), matching TOON conventions where type information is controlled via annotations. - ToonWriter bug fixes — type metadata indentation,
char[]serialization, and complex map key StackOverflow resolved. - Significant performance optimizations across ToonReader, ToonWriter, JsonWriter, and JsonObject hot paths.
- JSON5 added as 5th serialization test path — all existing round-trip tests now automatically cover JSON5 format.
Features
@IoShowType— field-level annotation that forces@type/$typeemission on the annotated field's value and its elements (Collections, Maps, arrays), regardless ofshowTypeInfoNever(), JSON5, or TOON mode. Essential for polymorphic fields. Jackson's@JsonTypeInfoon a field acts as a fallback synonym..json5()onWriteOptionsBuildernow setsshowTypeInfoNever()andcycleSupport(false)as defaults, aligning JSON5 with TOON conventions. Individual settings can still be overridden after calling.json5().- Spring auto-configuration — now provides a separate
toonWriteOptionsbean withcycleSupport(false)for TOON/JSON5 formats, in addition to the primaryjsonIoWriteOptionsbean.
Bug Fixes
ToonWriter— nested collections and arrays with type metadata (showTypeInfoAlways()) now emit properly indented$type/$itemsblocks. Previously, the compactfieldName[N]:path bypassedwriteCollection()/writeArray()entirely.ToonWriter—char[]fields now serialize as a plain string value instead of comma-separated characters, matching the Converter'sString → char[]read path.ToonWriter— maps with complex keys (e.g.,HashMapas key) no longer causeStackOverflowError. Routes towriteMap()with@keys/@valuesformat instead ofwriteMapInline().EnumSetFactory— infers enum element type from field generic declaration (EnumSet<Color>→Color.class). Empty EnumSets now round-trip correctly when field provides generic type information.
Improvements
- Field-context type resolution — enums, integer map keys, and EnumSets work consistently across JSON and TOON via generic type inference from
seedIncrementalContainerMetadata(). JsonObject— internal storage mode consolidated from two boolean flags into a single byte with four named states; cachedeffectiveValueseliminates repeated conditional dispatch.- TOON same-type round-trip — Converter-supported types written as bare strings without
$type; read back uses String → OriginalType converter path.
Performance
JsonIo.toToon()— defaults tocycleSupport(false), skippingtraceReferences()pre-pass for ~35-40% faster TOON serialization.ToonReader.cacheSubstring()— probes cache by source range before materializing substring, eliminating allocation on cache hits.ToonReader— lazy trimmed line materialization, buffer-direct scalar parsing, and combinedfield[N]...header parsing from existing slices.ToonReader— TOON string/input-stream reads reuseFastReadercharacter buffer via scoped buffer recycler.ToonWriter— tabular POJO detection validates uniformity viaWriteFieldPlanaccessors; row values streamed from accessors, eliminating N intermediate map allocations.JsonWriter— tracking structures allocated lazily based oncycleSupportmode, avoiding ~6 KB wasted allocations when disabled.JsonWriter.writeCollectionElement()— fast-pathsInteger,Float,Short,Bytedirectly towritePrimitive().JsonWriter.writeCollection()— indexedlist.get(i)forRandomAccesslists instead ofIteratorallocation.JsonObject— deferredkeys[]/values[]allocation using shared empty sentinel;appendFieldForParser()avoids redundant null assignments.
Testing
- Added TOON as 4th and JSON5 as 5th serialization path in
TestUtil, enabling all existing round-trip tests to automatically cover both formats. JSON5: 1 write fail (same custom writer edge case as json-io), 0 read fails.
Install
Maven
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<version>4.98.0</version>
</dependency>Gradle
implementation 'com.cedarsoftware:json-io:4.98.0'4.97.0
Highlights
- ToonReader performance optimization release — systematic hot-path improvements reducing per-line allocations, autoboxing, and field access overhead across the TOON parsing pipeline.
- Jackson test dependency updated from 2.19.4 to 2.21.1.
Performance
ToonReader.peekLine()— avoids materializing aStringfor blank, comment, and indent-only lines, reducing per-lineStringallocations by ~50%.ToonReader.cacheSubstring()— simplified to reduce code complexity and improve maintainability of the string caching layer.ToonReader.parseNumber()— integer fast path now avoids autoboxing by returning primitive values directly.JsonObject— index build deferred to first lookup instead of construction time;ToonReader.lineBuflocalized to reduce field access overhead.ToonReader.cacheSubstring()— inlined at hot call sites; frequently accessed fields (stringCache,classLoader) localized to reduce field dereference cost.ToonReader.readInlineObject()— char comparisons optimized;stringCachearray localized to a local variable in hot parsing methods.ToonReader— cache arrays (stringCache,numberCacheKeys,numberCacheValues) now reused across parse calls viaThreadLocal, eliminating per-parse array allocation.ToonReader.trimAsciiRange()— fast path that skips trim logic when the input range is already trimmed.ToonReader.parseNumber()— number cache arrays localized to reduce field access in the hot number-parsing loop.ToonReader.readInlineObject()— two separate indent checks merged into a single!=comparison.
Maintenance
- Update Jackson test dependency from 2.19.4 to 2.21.1; remove redundant
jackson-coreandjackson-annotationsdeclarations (pulled transitively viajackson-databind).
Install
Maven
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<version>4.97.0</version>
</dependency>Gradle
implementation 'com.cedarsoftware:json-io:4.97.0'4.96.0
Highlights
- 25 annotations for declarative serialization control (
@IoProperty,@IoIgnore,@IoCreator,@IoValue,@IoFormat,@IoNaming, and more) — with Jackson annotation compatibility at zero compile-time cost - Spring Boot starter — expanded YAML configuration for
WriteOptionsandReadOptionswithout writing Java code - TOON format enhancements — tabular POJO format, configurable delimiters, cycle support with
$id/$ref, and full type metadata emission - Significant performance optimizations across parser, writer, resolver, and injector hot paths
Features
- Spring Boot starter — expanded YAML configuration properties. All commonly used
WriteOptions/ReadOptionssettings now configurable viaapplication.yml. New write properties:force-map-output-as-two-arrays,write-enum-as-json-object,cycle-support,json5,date-format,indentation-size,show-root-type-info,meta-prefix,toon-delimiter. New read properties:use-unsafe,floating-point,integer-type. AnnotationResolver— 25 annotations incom.cedarsoftware.io.annotationfor full declarative serialization control:@IoProperty,@IoIgnore,@IoIgnoreProperties,@IoAlias,@IoPropertyOrder,@IoInclude,@IoCreator,@IoValue,@IoNaming,@IoIncludeProperties,@IoIgnoreType,@IoTypeInfo,@IoDeserialize,@IoClassFactory,@IoGetter,@IoSetter,@IoNonReferenceable,@IoNotCustomReader,@IoNotCustomWritten,@IoCustomWriter,@IoCustomReader,@IoTypeName,@IoAnySetter,@IoAnyGetter,@IoFormat.- Jackson annotation compatibility — json-io reflectively honors Jackson annotations when the Jackson JAR is on the classpath with zero compile-time dependency. json-io annotations take priority.
ToonWriter— tabular format for POJO collections/arrays (compact CSV-style[N]{field1,field2,...}: row1 row2 ...).ToonWriter— configurable delimiters (comma, tab, pipe) encoded in count bracket for auto-detection on read.ToonWriter— TOON type metadata emission (@type/@t/$type/$t) when type output is enabled.ToonWriter— defaults TOON metadata keys to$variants ($id/$ref/$items/$keys) for JSON5-friendly output.ToonWriter— usesWriteFieldPlan/Accessorabstraction, giving TOON automatic support for all write-side annotations.
Improvements
@typeelimination — now considers@IoDeserialize(as=X)and@IoTypeInfo(X)annotations, producing smaller JSON without sacrificing round-trip fidelity.AnnotationResolver— usesClassUtilities.forName()for proper classloader resolution in OSGi/JPMS environments.
Performance
Accessor.retrieve()— sticky fallback flags for lambda/VarHandle/MethodHandle paths; failed paths bypassed on subsequent calls.Injector— numeric kind and primitive-wrapper lookups now useClassValueMapO(1) dispatch.Injector.inject()— assignability-based pre-conversion reduces exception-driven control flow.JsonIo— thread-local buffer recycling in String-based JSON paths removes repeated stream/buffer construction.JsonObject.appendFieldForParser()— parser-only fast path avoidsindexOfduplicate-key search.JsonParser— read numeric pipeline optimized with directDouble.parseDouble()fast path for DOUBLE mode and primitive-array direct assignment.JsonParser— ASCII-only lowercase conversion, lookup-table hex parsing, bounded string dedupe caching, simplified strict-mode handling.JsonWriter— string escaping with precomputed control escape strings; unified smart-quote decision logic.JsonWriter— class-cached custom-writer gate checks viaClassValueMap.JsonWriter—cycleSupport(false)bypassesobjsReferenced/@idlookup across all write hot paths.JsonWriter.writeMapBody()— hoisted invariants reduce per-entry branch checks.Resolver— target-kind based scalar coercion with precomputed kinds in array/field hot loops and direct no-allocation primitive array writes.ReadOptionsBuilder/WriteOptionsBuilder— cached injector/write-field planning reduces repeated reflection/generic analysis.
Bug Fixes
JsonWriter.write()— always clearsobjVisited/objsReferencedinfinallyblock, preventing state leakage.JsonWriter.traceReferences()— fixed off-by-one in object-limit enforcement.JsonWriter/ToonWriter—cycleSupport(false)throwsJsonIoExceptionwith remediation hint;cycleSupport(true)retains normal id/ref handling.ObjectResolver— map generic typing preserves both key and value generic types during traversal.ObjectResolver.processJsonObjectElement()— preserves explicit element@typemetadata in arrays for polymorphic deserialization.ObjectResolver— generic inference now runs incrementally instead of deepmarkUntypedObjects()pre-pass.ObjectResolver.assignField()— applies@IoDeserialize/@IoTypeInfooverrides for TOON list-style arrays/maps.Resolver.wrapException()— preserves causal chain withJsonIoException(message, cause).Resolver.toJava()— supports TOON key/value entry-list conversion for concrete Map subclasses.ToonWriter— applies@IoPropertyrename and@IoPropertyOrderreordering.ToonWriter— indents nested referenced-map id metadata correctly.ToonReader— recognizes all TOON metadata key variants for full id/ref identity round-trip.
Install
Maven
<dependency>
<groupId>com.cedarsoftware</groupId>
<artifactId>json-io</artifactId>
<version>4.96.0</version>
</dependency>Gradle
implementation 'com.cedarsoftware:json-io:4.96.0'