Skip to content

Releases: jdereg/json-io

4.101.0

19 Apr 22:20

Choose a tag to compare

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 setting showTypeInfoNever(), 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_CASE and LOWER_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/@items parallel-array form. Default false for JSON (backward-compat), true for JSON5.
  • WriteOptionsBuilder.preserveLeafContainerIdentity(boolean) — when false (the new default), traceReferences skips containers holding only non-referenceable leaves (List<String>, Map<UUID, Date>, String[], etc.), matching Jackson's default identity semantics.
  • WriteOptionsBuilder.writeOptionalAsObject(boolean) — set to true for legacy {"present":true,"value":X} form; default false writes 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/ClassValueSet lookups to the new getByClass(Class) / containsClass(Class) fast-path APIs (added in java-util 4.101.0).
  • AnnotationResolver.getMetadata — split into trivially-inlineable fast path + scanAndCache cold path. Fast-path self-time dropped ~50%.
  • ObjectResolver.traverseFields — hoisted per-object AnnotationResolver.getMetadata call out of the per-field loop (~20% of Resolver-phase samples).
  • ReadOptions.isNonReferenceableClass / isNotCustomReaderClass — memoized via per-instance ClassValue<Boolean> caches (mirrors WriteOptions.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 at WriteFieldPlan build time (eliminates per-field ConcurrentHashMap.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 — skips hasComplexKeys iteration when the enclosing field's declared Map key type is Converter-stringifiable.
  • JsonWriter.writeCollectionElement — POJO short-circuit for cycleSupport=false collapses 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 for Optional ({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 used int q which silently truncated Long.MIN_VALUE (rendered as -206158430208 instead of -9223372036854775808). Fixed by using long q.

Jackson-alignment (opt-in)

  • WriteOptionsBuilder.standardJson() and json5() now also configure ISO-8601 date formatting for java.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

10 Apr 13:30

Choose a tag to compare

4.100.0 - 2026-04-10

Bug Fixes

  • Fixed deadlock from circular static initialization between ReadOptionsBuilder and WriteOptionsBuilder. When two threads concurrently triggered class loading (e.g., one calling JsonIo.toJson() and another calling JsonIo.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 through MetaUtils bootstrap methods.

Dependency Updates

  • java-util 4.100.0 (includes fix for unbounded Converter.FULL_CONVERSION_CACHE memory leak)
  • JUnit 5.14.2 → 5.14.3
  • Jackson 2.21.1 → 2.21.2

4.99.0

31 Mar 00:47

Choose a tag to compare

4.99.0 - 2026-03-28

  • PERFORMANCE: ToonWriter.needsQuoting() — replaced multi-method dispatch chain with a single-pass scanNeedsQuoting() method. Uses a pre-built boolean[128] lookup table per delimiter. JFR shows needsQuoting dropped from 383 samples to 25 — a 93.5% reduction.
  • PERFORMANCE: ToonReader.readLineRaw() — now uses FastReader.readLine() instead of readUntil() + separate read() + 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 on cycleSupport mode. When cycleSupport=false (default), saves ~6 KB per writer. TOON write time improved 4-7%, reaching 2.02x Jackson.
  • PERFORMANCE: ToonReader.findColonInBuf() — single-pass scan with c <= ':' range guard. JFR shows 8-11% improvement in TOON read time.
  • PERFORMANCE: ToonReader string/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-paths Boolean, Double, Long, Integer, Float, Short, Byte, and String elements directly to writePrimitive()/writeStringValue(), bypassing the writeImpl() dispatch chain.
  • CLEANUP: Removed unused hasSameKeyOrder() method from ToonWriter.
  • CLEANUP: Removed two unreachable dead branches from writeFieldEntry() and writeFieldEntryInline().
  • CLEANUP: Fixed Javadoc tag error in getObjectFields().

4.98.0

08 Mar 23:07

Choose a tag to compare

Highlights

  • @IoShowType annotation — new field-level annotation (26th) that forces @type/$type emission on polymorphic fields regardless of showTypeInfo mode. Jackson's @JsonTypeInfo on a field is honored as a fallback synonym.
  • JSON5 defaults alignment.json5() now sets showTypeInfoNever() and cycleSupport(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/$type emission on the annotated field's value and its elements (Collections, Maps, arrays), regardless of showTypeInfoNever(), JSON5, or TOON mode. Essential for polymorphic fields. Jackson's @JsonTypeInfo on a field acts as a fallback synonym.
  • .json5() on WriteOptionsBuilder now sets showTypeInfoNever() and cycleSupport(false) as defaults, aligning JSON5 with TOON conventions. Individual settings can still be overridden after calling .json5().
  • Spring auto-configuration — now provides a separate toonWriteOptions bean with cycleSupport(false) for TOON/JSON5 formats, in addition to the primary jsonIoWriteOptions bean.

Bug Fixes

  • ToonWriter — nested collections and arrays with type metadata (showTypeInfoAlways()) now emit properly indented $type/$items blocks. Previously, the compact fieldName[N]: path bypassed writeCollection()/writeArray() entirely.
  • ToonWriterchar[] fields now serialize as a plain string value instead of comma-separated characters, matching the Converter's String → char[] read path.
  • ToonWriter — maps with complex keys (e.g., HashMap as key) no longer cause StackOverflowError. Routes to writeMap() with @keys/@values format instead of writeMapInline().
  • 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; cached effectiveValues eliminates 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 to cycleSupport(false), skipping traceReferences() 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 combined field[N]... header parsing from existing slices.
  • ToonReader — TOON string/input-stream reads reuse FastReader character buffer via scoped buffer recycler.
  • ToonWriter — tabular POJO detection validates uniformity via WriteFieldPlan accessors; row values streamed from accessors, eliminating N intermediate map allocations.
  • JsonWriter — tracking structures allocated lazily based on cycleSupport mode, avoiding ~6 KB wasted allocations when disabled.
  • JsonWriter.writeCollectionElement() — fast-paths Integer, Float, Short, Byte directly to writePrimitive().
  • JsonWriter.writeCollection() — indexed list.get(i) for RandomAccess lists instead of Iterator allocation.
  • JsonObject — deferred keys[]/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

08 Mar 23:06

Choose a tag to compare

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 a String for blank, comment, and indent-only lines, reducing per-line String allocations 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.lineBuf localized 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; stringCache array localized to a local variable in hot parsing methods.
  • ToonReader — cache arrays (stringCache, numberCacheKeys, numberCacheValues) now reused across parse calls via ThreadLocal, 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-core and jackson-annotations declarations (pulled transitively via jackson-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

01 Mar 14:17

Choose a tag to compare

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 WriteOptions and ReadOptions without 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/ReadOptions settings now configurable via application.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 in com.cedarsoftware.io.annotation for 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 — uses WriteFieldPlan/Accessor abstraction, giving TOON automatic support for all write-side annotations.

Improvements

  • @type elimination — now considers @IoDeserialize(as=X) and @IoTypeInfo(X) annotations, producing smaller JSON without sacrificing round-trip fidelity.
  • AnnotationResolver — uses ClassUtilities.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 use ClassValueMap O(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 avoids indexOf duplicate-key search.
  • JsonParser — read numeric pipeline optimized with direct Double.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 via ClassValueMap.
  • JsonWritercycleSupport(false) bypasses objsReferenced/@id lookup 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 clears objVisited/objsReferenced in finally block, preventing state leakage.
  • JsonWriter.traceReferences() — fixed off-by-one in object-limit enforcement.
  • JsonWriter / ToonWritercycleSupport(false) throws JsonIoException with 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 @type metadata in arrays for polymorphic deserialization.
  • ObjectResolver — generic inference now runs incrementally instead of deep markUntypedObjects() pre-pass.
  • ObjectResolver.assignField() — applies @IoDeserialize/@IoTypeInfo overrides for TOON list-style arrays/maps.
  • Resolver.wrapException() — preserves causal chain with JsonIoException(message, cause).
  • Resolver.toJava() — supports TOON key/value entry-list conversion for concrete Map subclasses.
  • ToonWriter — applies @IoProperty rename and @IoPropertyOrder reordering.
  • 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'