Skip to content

Latest commit

 

History

History
1542 lines (1516 loc) · 172 KB

File metadata and controls

1542 lines (1516 loc) · 172 KB

Revision History

4.99.0

  • PERFORMANCE: JsonWriter.writeObjectArray() now fast-paths Boolean, Double, Long, Integer, Float, Short, Byte, and String elements directly to writePrimitive()/writeStringValue(), bypassing the writeImpl()writeCustom() dispatch chain when @type is not needed. Mirrors the existing writeCollectionElement() optimization. JFR shows writeArrayElementIfMatching dropped from 62 samples to 0.

4.98.0 - 2026-03-08

  • BUG FIX: 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, and the inline list-element path wrote $type/$items at incorrect indentation levels. Fixed in writeFieldEntry(), writeFieldEntryInline(), writeFoldedEntry(), and writeListElement().
  • BUG FIX: ToonWriterchar[] fields now serialize as a plain string value instead of comma-separated characters, matching the Converter's String → char[] read path for correct round-trips.
  • BUG FIX: ToonWriter — maps with complex keys (e.g., a HashMap used as a key in another HashMap) no longer cause StackOverflowError. writeListElement() now checks hasComplexKeys() and routes to writeMap() (which uses @keys/@values format with cycle detection) instead of writeMapInline() which called key.toString() unconditionally.
  • BUG FIX: EnumSetFactory — now infers the enum element type from the field's generic declaration (e.g., EnumSet<Color>Color.class) via JsonObject.getItemElementType(). Previously, empty EnumSets or EnumSets without explicit @type on items could not round-trip through JSON/TOON when the field provided generic type information. Legacy RegularEnumSet JSON without @items now deserializes to an empty EnumSet instead of null when field context provides the enum class.
  • IMPROVEMENT: Field-context type resolution for enums, integer map keys, and EnumSets now works consistently across both JSON and TOON formats. List<Color>, Map<Integer, String>, and EnumSet<Color> fields all round-trip correctly using generic type inference from seedIncrementalContainerMetadata() — no explicit @type/$type needed in the stream.
  • IMPROVEMENT: JsonObject internal storage mode consolidated from two boolean flags (keysWereSet/itemsWereSet) into a single byte with four named states (MODE_POJO, MODE_ITEMS, MODE_KEYS_ONLY, MODE_KEYS_ITEMS), and a cached effectiveValues reference eliminates repeated conditional dispatch across 10 hot-path methods.
  • IMPROVEMENT: TOON same-type round-trip design — TOON writes Converter-supported types as bare strings without $type metadata. Reading back via .asClass(OriginalType) uses the Converter's String → OriginalType path and works for all supported types. Cross-type conversion (e.g., write ZonedDateTime, read as Long) is not supported directly through TOON; users should read back as the original type first, then use the Converter for cross-type needs.
  • PERFORMANCE: JsonIo.toToon() now defaults to cycleSupport(false) when null is passed for WriteOptions, skipping the traceReferences() pre-pass for ~35-40% faster TOON serialization. TOON targets LLM communication where data is typically acyclic; if a cycle is encountered, a clear JsonIoException is thrown with guidance to enable cycleSupport(true).
  • PERFORMANCE: ToonReader.cacheSubstring() now probes its string cache by source range before materializing a substring, eliminating allocation on cache hits in hot key/value parsing paths.
  • PERFORMANCE: ToonReader now loads line metadata without eagerly materializing trimmed line strings; peekTrimmed() creates them lazily only when the caller needs line text.
  • PERFORMANCE: TOON string/input-stream reads now reuse the main FastReader character buffer via JsonIo's scoped buffer recycler, reducing per-parse reader-buffer allocation without sharing mutable buffers across live parsers.
  • PERFORMANCE: ToonReader now parses common unquoted scalar values directly from source ranges in object, list, inline-array, and tabular read paths, avoiding intermediate String creation before boolean/number classification.
  • PERFORMANCE: ToonReader now parses combined field[N]... array headers directly from existing key/value slices instead of rebuilding synthetic array strings, reducing TOON maps-read overhead for inline and tabular object fields.
  • PERFORMANCE: JsonObject default constructor now defers keys[]/values[] allocation using a shared empty sentinel, avoiding 256 bytes of wasted arrays for array/collection nodes that only use items[] storage.
  • PERFORMANCE: JsonObject.appendFieldForParser() no longer redundantly nulls hash and index fields that are already null during parsing.
  • PERFORMANCE: ToonWriter tabular POJO detection now validates uniformity directly via WriteFieldPlan accessors instead of creating a LinkedHashMap per element via getObjectFields(). Row values are streamed from accessors during the write phase, eliminating N intermediate map allocations per tabular array.
  • PERFORMANCE: JsonWriter tracking structures (objVisited, objsReferenced, traceDepths vs activePath) are now allocated lazily based on cycleSupport mode, avoiding ~6 KB of wasted allocations per writer when cycle support is disabled.
  • PERFORMANCE: JsonWriter.writeCollectionElement() now fast-paths Integer, Float, Short, and Byte directly to writePrimitive(), bypassing the writeImpl()writeCustom() dispatch chain when @type is not needed.
  • PERFORMANCE: JsonWriter.writeCollection() uses indexed list.get(i) for RandomAccess lists (e.g., ArrayList) instead of allocating an Iterator. writeObject() and enum field loops also use indexed iteration to avoid implicit Iterator allocation.
  • FEATURE: @IoShowType — new field-level annotation (26th annotation) that forces @type/$type emission on the annotated field's value and its elements (Collections, Maps, arrays), regardless of the global showTypeInfo setting. Essential for polymorphic fields when using showTypeInfoNever(), JSON5 mode, or TOON format. Jackson's @JsonTypeInfo on a field is honored as a fallback synonym.
  • IMPROVEMENT: .json5() on WriteOptionsBuilder now sets showTypeInfoNever() and cycleSupport(false) as defaults, aligning JSON5 with TOON conventions where type information is controlled via annotations rather than embedded metadata. Individual settings can still be overridden after calling .json5().
  • IMPROVEMENT: Spring auto-configuration now provides a separate toonWriteOptions bean with cycleSupport(false) for TOON/JSON5 formats, in addition to the primary jsonIoWriteOptions bean.
  • 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 write: 1 fail (same custom writer edge case as json-io), JSON5 read: 0 fails.

4.97.0 - 2026-03-03

  • PERFORMANCE: ToonReader.peekLine() now avoids materializing a String for blank, comment, and indent-only lines, reducing per-line String allocations by ~50%.
  • PERFORMANCE: ToonReader.cacheSubstring() simplified to reduce code complexity and improve maintainability of the string caching layer.
  • PERFORMANCE: ToonReader.parseNumber() integer fast path now avoids autoboxing by returning primitive values directly.
  • PERFORMANCE: JsonObject index build is now deferred to the first lookup instead of construction time; ToonReader.lineBuf localized to reduce field access overhead.
  • PERFORMANCE: ToonReader.cacheSubstring() inlined at hot call sites; frequently accessed fields (stringCache, classLoader) localized to reduce field dereference cost.
  • PERFORMANCE: ToonReader.readInlineObject() char comparisons optimized; stringCache array localized to a local variable in hot parsing methods.
  • PERFORMANCE: ToonReader cache arrays (stringCache, numberCacheKeys, numberCacheValues) now reused across parse calls via ThreadLocal, eliminating per-parse array allocation.
  • PERFORMANCE: ToonReader.trimAsciiRange() now includes a fast path that skips trim logic when the input range is already trimmed.
  • PERFORMANCE: ToonReader.parseNumber() number cache arrays localized to reduce field access in the hot number-parsing loop.
  • PERFORMANCE: 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).

4.96.0 - 2026-02-28

  • FEATURE: Spring Boot starter — expanded YAML configuration properties. All commonly used WriteOptions and ReadOptions settings are now configurable via application.yml without writing Java code. 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. Advanced/internal tuning settings remain available through ReadOptionsCustomizer/WriteOptionsCustomizer beans.
  • IMPROVEMENT: @type elimination on write now considers @IoDeserialize(as=X) and @IoTypeInfo(X) annotations. When the runtime type of a field matches the annotation-specified type, @type is omitted from JSON output because the reader can infer the correct type from the annotation. This produces smaller JSON without sacrificing round-trip fidelity.
  • IMPROVEMENT: AnnotationResolver now uses ClassUtilities.forName() instead of Class.forName() for external annotation detection, ensuring proper classloader resolution in OSGi and JPMS environments.
  • FEATURE: ToonWriter now uses the same WriteFieldPlan / Accessor abstraction as JsonWriter. This gives TOON serialization automatic support for @IoGetter, @IoFormat, @IoAnyGetter, and all other write-side annotations. Previously ToonWriter used direct Field.get() which bypassed custom getter methods and format patterns.
  • PERFORMANCE: Accessor.retrieve() now uses sticky fallback flags for lambda/VarHandle/MethodHandle paths; once an optimized path fails, it is bypassed on subsequent calls instead of repeatedly throwing/falling back.
  • FEATURE: AnnotationResolver — annotation-based serialization control. json-io now supports 25 annotations in com.cedarsoftware.io.annotation:
    • @IoProperty("name") — rename a field in JSON output and accept the renamed key on read.
    • @IoIgnore — exclude a field from serialization and deserialization.
    • @IoIgnoreProperties({"a","b"}) — class-level field exclusion by name.
    • @IoAlias({"alt1","alt2"}) — accept alternate JSON property names on read.
    • @IoPropertyOrder({"x","y"}) — control field order during serialization.
    • @IoInclude(Include.NON_NULL) — per-field null skipping on write.
    • @IoCreator — mark a constructor or static factory method for deserialization (supports @IoProperty on parameters for JSON key mapping).
    • @IoValue — mark a no-arg instance method for single-value serialization (e.g., EmailAddress"user@example.com").
    • @IoNaming(Strategy.SNAKE_CASE) — class-level naming strategy (SNAKE_CASE, KEBAB_CASE, UPPER_CAMEL_CASE, LOWER_DOT_CASE). @IoProperty on individual fields overrides the strategy.
    • @IoIncludeProperties({"a","b"}) — class-level whitelist of fields to include (inverse of @IoIgnoreProperties).
    • @IoIgnoreType — class-level annotation that excludes all fields of this type from serialization/deserialization across all classes.
    • @IoTypeInfo(LinkedList.class) — field-level default concrete type when no @type is present in JSON (fallback hint for polymorphic fields).
    • @IoDeserialize(as = LinkedList.class) — field-level or class-level forced type override during deserialization. Priority: @type in JSON > @IoDeserialize > @IoTypeInfo > declared field type.
    • @IoClassFactory(MyFactory.class) — class-level annotation specifying a ClassFactory implementation for custom deserialization. Factory instances are cached and shared. Programmatic addClassFactory() takes priority.
    • @IoGetter("fieldName") — method-level annotation marking a no-arg instance method as the getter for a field during serialization. Replaces the standard getXxx() convention. Programmatic addNonStandardGetter() takes priority.
    • @IoSetter("fieldName") — method-level annotation marking a 1-arg instance method as the setter for a field during deserialization. Replaces the standard setXxx() convention. Programmatic addPermanentNonStandardSetter() takes priority.
    • @IoNonReferenceable — class-level annotation marking a type as non-referenceable. Instances will never emit @id/@ref pairs. Annotation equivalent of config/nonRefs.txt and addNonReferenceableClass().
    • @IoNotCustomReader — class-level annotation that suppresses custom reader usage for this type, even if a custom reader exists for a parent class. Annotation equivalent of config/notCustomRead.txt and addNotCustomReaderClass().
    • @IoNotCustomWritten — class-level annotation that suppresses custom writer usage for this type, even if a custom writer exists for a parent class. Annotation equivalent of config/notCustomWritten.txt and addNotCustomWrittenClass().
    • @IoCustomWriter(MyWriter.class) — class-level annotation specifying a JsonClassWriter implementation for custom serialization. Writer instances are cached and shared. Programmatic addCustomWrittenClass() takes priority.
    • @IoCustomReader(MyReader.class) — class-level annotation specifying a JsonClassReader implementation for custom deserialization. Reader instances are cached and shared. Programmatic addCustomReaderClass() takes priority.
    • @IoTypeName("ShortName") — class-level annotation assigning a short alias for @type in JSON. Equivalent of aliases.txt and aliasTypeName(). On write, emits the alias instead of the FQCN; on read, resolves the alias back to the class. Programmatic aliasTypeName() takes priority.
    • @IoAnySetter — method-level annotation marking a 2-arg instance method (String key, Object value) as the receiver for unrecognized JSON fields during deserialization. Takes priority over the global MissingFieldHandler. Equivalent to Jackson's @JsonAnySetter.
    • @IoAnyGetter — method-level annotation marking a no-arg instance method returning Map<String, Object> as the source of extra fields during serialization. Extra fields are written after regular declared fields. Equivalent to Jackson's @JsonAnyGetter.
    • @IoFormat("pattern") — field-level annotation specifying a format pattern for date/time, numeric, or any type. Three pattern engines are auto-detected: (1) C-style String.format() patterns containing % (e.g., "%,d", "%.2f", "%05d", "%x", "%10s") — works on any type; (2) DecimalFormat patterns (e.g., "#,###", "$#,##0.00", "0.00") — for numeric types; (3) DateTimeFormatter/SimpleDateFormat patterns (e.g., "dd/MM/yyyy", "yyyy-MM-dd HH:mm") — for date/time types. Formatted values are written as JSON strings and parsed back on read. Supports String, LocalDate, LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, OffsetTime, Instant, java.util.Date, int, long, double, float, short, byte (and wrappers), BigDecimal, BigInteger, AtomicInteger, AtomicLong. Annotations are scanned once per class and cached in a ClassValueMap. Programmatic API always overrides annotations.
  • FEATURE: AnnotationResolver — 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 over Jackson annotations on the same element. Supported Jackson annotations:
    • @JsonProperty, @JsonIgnore, @JsonIgnoreProperties, @JsonAlias, @JsonPropertyOrder, @JsonInclude(Include.NON_NULL) (jackson-annotations)
    • @JsonCreator, @JsonValue, @JsonIgnoreType, @JsonTypeInfo(defaultImpl=...), @JsonIncludeProperties, @JsonGetter, @JsonSetter, @JsonTypeName, @JsonFormat(pattern="..."), @JsonAnySetter, @JsonAnyGetter (jackson-annotations)
    • @JsonNaming(SnakeCaseStrategy.class), @JsonDeserialize(as=...) (jackson-databind)
  • PERFORMANCE: Injector numeric kind and primitive-wrapper lookups now use ClassValueMap O(1) dispatch instead of sequential class == chains.
  • PERFORMANCE: Injector.inject() now applies assignability-based pre-conversion before reflective set/invoke, reducing exception-driven control flow in hot assignment paths while preserving fallback conversion behavior.
  • PERFORMANCE: JsonIo — added thread-local buffer recycling in String-based JSON paths:
    • toJson(Object, WriteOptions) now reuses FastByteArrayOutputStream and FastWriter buffers.
    • toJava(String, ...) and toJava(InputStream, ...) builder paths now reuse FastReader char/pushback buffers. This removes repeated stream/buffer construction churn in benchmark loops and reduces allocation pressure.
  • PERFORMANCE: JsonObject.appendFieldForParser()JsonParser now uses a parser-only fast path for object field insertion, avoiding JsonObject.put() duplicate-key search (indexOf) on the common unique-key path. This reduces parse-time key insertion overhead and shifts work from linear duplicate checks to direct append.
  • MAINTENANCE: JsonParser — removed redundant strict-mode checks in skipWhitespaceRead() that were unreachable after strict-branch early return.
  • PERFORMANCE: JsonParser — read numeric pipeline optimized:
    • Floating-point parsing now takes a direct Double.parseDouble() fast path when configured for DOUBLE mode, bypassing minimal-number analysis in the default path.
    • ObjectResolver.traverseArray() now has a primitive-array direct assignment path for parsed JSON Long/Double values, avoiding intermediate wrapper coercion in hot loops.
  • PERFORMANCE: JsonParser — hot paths optimized with ASCII-only lowercase conversion for token reads, lookup-table hex parsing, bounded string dedupe caching, and simplified strict-mode whitespace/comment handling.
  • BUG FIX: JsonWriter.write() now always clears objVisited/objsReferenced in a finally block, preventing state leakage and object retention if write/flush throws.
  • BUG FIX: JsonWriter.traceReferences() object-limit handling now enforces limits without off-by-one behavior at the configured boundary.
  • BUG FIX: JsonWriter now enforces explicit cycle policy by option: cycleSupport(false) throws JsonIoException on cyclic graphs with a remediation hint to enable cycleSupport(true), while cycleSupport(true) retains normal @id/@ref handling.
  • PERFORMANCE: JsonWriter — string escaping/writing hot paths optimized: removed String.format("\\u%04x", ...) from control-char escaping, added precomputed control escape strings, and unified smart-quote decision logic to avoid duplicated scanning code.
  • PERFORMANCE: JsonWriter — custom-writer gate checks are now class-cached via shared DefaultWriteOptions caches (using ClassValueMap):
    • Added per-declared-class gate cache for isNotCustomWrittenClass and declared custom writer resolution.
    • Runtime custom writer resolution now reuses the existing getCustomWriter()/writerCache path after gate pass (no duplicate runtime cache layer). This lowers hot-loop cost in writeUsingCustomWriter() / getCustomWriterIfAllowed(), avoids per-JsonWriter cache construction churn in short-lived writer workloads, and removes redundant runtime cache indirection.
  • PERFORMANCE: JsonWriter — now consistently honors cycleSupport(false) by bypassing objsReferenced / @id lookup work across write hot paths (object/map/collection/array/custom writer) while using lightweight active-path checks for cycle detection.
  • PERFORMANCE: JsonWriter.writeMapBody(...) (both Iterator and JsonObject paths) now uses specialized loop branches with hoisted invariants (skipNullFields, json5UnquotedKeys, maxStringLength) to reduce per-entry branch checks in map write hot loops.
  • MAINTENANCE/PERFORMANCE: MapResolver — inlined and removed now-redundant fastPrimitiveCoercion(...) wrapper after scalar coercion consolidation.
  • BUG FIX: ObjectResolver — map generic typing now preserves and applies both key and value generic types during traversal, fixing cases where complex map keys/values could remain as JsonObject instead of resolving to target types.
  • BUG FIX: ObjectResolver.processJsonObjectElement() now preserves explicit element @type metadata in arrays by only applying declared component type when the element type is missing. This fixes polymorphic array deserialization where explicit subtype entries could be overwritten by the declared array component type.
  • BUG FIX / PERFORMANCE: ObjectResolver — generic inference now runs incrementally (on-demand) and no longer relies on the deep markUntypedObjects() pre-pass. This reduces upfront traversal work while preserving nested generic correctness across parameterized object fields, collections, and maps.
  • MAINTENANCE: ObjectResolver — scalar fast-path conversion gates now use converter.isConversionSupportedFor(source, target) in both assignField() and readWithFactoryIfExists() (with existing scalar/simple checks retained), reducing dependence on the pair-form "simple type" API without changing behavior.
  • MAINTENANCE/PERFORMANCE: ObjectResolver — nested collection type-marking path was cleaned up by removing an unused collectionClass parameter and eliminating per-nested-array ArrayList copy allocation in handleArrayInCollection(). Nested arrays now reuse the existing JsonObject wrapper for traversal, reducing allocation churn in deep generic collection payloads.
  • BUG FIX: ObjectResolver.assignField() now consistently applies @IoDeserialize / @IoTypeInfo field overrides when TOON list-style arrays/maps arrive as Collection values, routing container data through resolver traversal instead of raw reflective injection.
  • CLEANUP: ReadOptions — removed legacy pre-pass code path and associated temporary API toggles (ReadOptions.useLegacyMarkUntypedObjectsPrepass() and ReadOptionsBuilder.useLegacyMarkUntypedObjectsPrepass(boolean)).
  • PERFORMANCE: ReadOptionsBuilder — added cached read injector planning (InjectorPlan) and wired ObjectResolver/Resolver field lookup paths to use it, reducing repeated map resolution overhead during traversal and unresolved-reference patching.
  • BUG FIX: Resolver.wrapException() now preserves the causal chain by constructing JsonIoException(message, cause) instead of creating a message-only wrapper. This restores root exception context for conversion failures.
  • MAINTENANCE: Resolver — replaced resolver/read-path usage of Converter "simple type" APIs with internal Resolver.isPseudoPrimitive(Class<?>) gating (string-convertible, non-container, non-enum scalar-like types). This reduces dependency on confusing Converter simple-type API surface while preserving behavior in Resolver, ObjectResolver, and MapResolver.
  • MAINTENANCE: Resolver.isPseudoPrimitive(Class<?>) refined to align with read-side semantics by checking isSimpleTypeConversionSupported(String.class, type) (materializable from JSON scalar strings), while still excluding enums.
  • MAINTENANCE: Resolver.toJava() — simplified map pre-conversion decision logic by consolidating overlapping conditions into focused helpers (isMapLikeJsonType, shouldEarlyConvertMapToNonMap, resolveMapPojoValueTypeForPreConversion, shouldConvertMapValueTypeAsPojo) with parity behavior for map-to-POJO and parameterized map value materialization paths.
  • BUG FIX: Resolver.toJava() now supports TOON key/value entry-list conversion for concrete Map subclasses by extracting generic key/value types from map superclass declarations, enabling typed map reconstruction without explicit @type metadata.
  • PERFORMANCE: Resolver — read-side scalar coercion hot paths refactored to avoid legacy coerceLong()/coerceDouble() indirection:
    • Added target-kind based scalar coercion (scalarTargetKind / fastScalarCoercion) in Resolver.
    • ObjectResolver / MapResolver now use precomputed scalar target kinds in array and field hot loops.
    • Primitive array assignment now uses direct no-allocation writes via shared tryAssignParsedScalarToArray(...). This reduces wrapper churn and removes repeated coercion dispatch overhead in numeric-heavy deserialization.
  • BUG FIX: ToonWriter now applies @IoProperty rename and @IoPropertyOrder reordering in its getObjectFields() path, ensuring annotations produce identical output across both JSON and TOON writers.
  • FEATURE: ToonWriter — now supports configurable delimiters for tabular arrays and inline primitive arrays. Supported delimiters: comma (,, default), tab (\t), and pipe (|). Configure via WriteOptionsBuilder.toonDelimiter(char) per-instance or WriteOptionsBuilder.addPermanentToonDelimiter(char) for JVM-wide defaults. The delimiter is encoded in the count bracket ([N], [N\t], [N|]) so the ToonReader auto-detects it on read — no read-side configuration needed. Tab delimiters can further reduce BPE token count for LLM payloads.
  • FEATURE: ToonWriter — now supports tabular format for POJO collections and arrays. When prettyPrint is false (default), uniform POJO lists/arrays are written in compact CSV-style tabular format ([N]{field1,field2,...}: row1 row2 ...). When prettyPrint is true, the verbose list format (- key: value) is used instead.
  • FEATURE: ToonWriter now defaults TOON metadata keys to $ variants ($id/$ref/$items/$keys) for JSON5-friendly output. useMetaPrefixAt() and useMetaPrefixDollar() overrides are still honored.
  • FEATURE: ToonWriter now emits TOON type metadata (@type/@t/$type/$t) when type output is enabled (showTypeInfoMinimal(), showTypeInfoMinimalPlus(), or showTypeInfoAlways()), including nested field values, arrays, collections, maps, map keys, and map values.
  • BEHAVIOR: JsonIo.toToon(..., null) now defaults TOON writing to showTypeInfoNever() so type metadata is omitted unless explicitly enabled via WriteOptions.
  • BUG FIX: ToonWriter now enforces explicit cycle policy by option: cycleSupport(false) throws JsonIoException on cyclic graphs, while cycleSupport(true) emits TOON id/ref metadata for shared references and cycles (including arrays, collections, maps, and POJOs) to preserve object identity.
  • BUG FIX: ToonWriter cycle exceptions in cycleSupport(false) mode now include a direct remediation hint to enable cycleSupport(true) for cyclic graph serialization.
  • BUG FIX: ToonWriter now indents nested referenced-map id metadata correctly (including complex-key maps), preventing metadata from being parsed at the wrong level.
  • BUG FIX: ToonReader now recognizes TOON metadata keys (@id/@ref/@items/@keys plus short and $ aliases) and loads them into resolver metadata/reference tracking, enabling full TOON id/ref identity round-trip during deserialization.
  • TESTING: Added TOON metadata variant coverage to verify all four key variants for id/ref/items/keys (@long, @short, $long, $short) with complex-key map identity round-trip assertions.
  • TESTING: Added a non-trivial TOON cycle graph round-trip test covering field-reference cycles, array self-cycles, map-key cycles, map-value cycles, and collection self-cycles.
  • TESTING: Added TOON reader/writer type-behavior tests covering .asClass() / .asType() hints, no-hint fallback-to-map behavior, @IoTypeInfo field hints, and type-enabled TOON round-trip across field, array, collection, map-key, and map-value polymorphic cases.
  • TESTING: Added TOON no-type annotation matrix coverage proving fail/fallback without hints and successful reconstruction via field annotations across field, array, collection, map-key, and map-value polymorphic scenarios.
  • MAINTENANCE: ToonWriter — removed unused private method writeCollectionElements(Collection<?>).
  • PERFORMANCE: WriteOptionsBuilder — added cached write-field planning (WriteFieldPlan) and switched JsonWriter object/enum/trace field loops to use precomputed per-class metadata (serialized key literal, declared container generic types, and trace-skip hints), reducing repeated reflection/generic analysis in hot paths.
  • TESTING: Added 17 new TOON format tests covering both tabular and list POJO format paths:
    • 10 writer tests: tabular default, prettyPrint list fallback, non-primitive field fallback, array tabular, mixed-type fallback, round-trip for both formats.
    • 7 reader tests: hand-authored tabular/list parsing, end-to-end write-then-read round-trips, both-formats-same-result verification.
  • TESTING: Added PolymorphicArrayElementTypeTest to verify explicit array element @type is honored over declared component type inference.
  • TESTING: Added ReadWithFactoryDeadCodeTest.testReadWithFactoryIfExists_primitiveStringMismatchUsesConversionSupportGate() to verify the primitive/String mismatch fast path uses conversion support checks and preserves null-return fallback for unsupported scalar targets.
  • TESTING: Added ResolverValueToTargetTest.conversionFailurePreservesCause() to assert conversion failure wrappers retain the original cause.
  • CLEANUP: Silenced verbose test output that cluttered Maven build logs:
    • JsonIoMainTest: Suppressed logger to prevent full conversion table dump to stdout.
    • CompactFormatTest: Removed 36 debug System.out.println calls; all 33 tests and assertions retained.
    • SkippedConversionsDebugTest: Replaced 60+ debug println calls with proper JUnit assertions; all 12 tests retained.
    • AutomaticColorTest, AutomaticFileTest, AutomaticPathTest: Removed verbose Logger/System.out.println debug output.
    • Issue423UnknownTypeTest, Issue425ExactReproTest, Issue425NestedJsonTest: Removed verbose debug output.
    • SecurityAuditLoggerTest: Fixed log handler lifecycle to suppress console output during tests.
  • BUG FIX: Updated java-util dependency to 4.96.0-SNAPSHOT which fixes a ClassUtilities.trySetAccessible() caching bug. The WeakHashMap-based accessibility cache used equals() lookup, causing different Field instances for the same logical field to share cache entries. This left fields inaccessible, causing Traverser to silently skip them and GraphComparator.applyDelta() to fail with "source object not found."
  • TEST FIX: EnumTests — updated testDuplicateRef and testEnumField to expect successful deserialization of non-static inner classes of package-private outer classes. These classes are correctly instantiable via setAccessible() on classpath (no JPMS); the previous test expectations relied on the now-fixed caching bug.

4.95.0 - not released

4.94.0 - 2026-02-14

  • FEATURE: Added TOON strict-read configuration to ReadOptions / ReadOptionsBuilder:
    • New ReadOptions.isStrictToon() getter.
    • New ReadOptionsBuilder.strictToon() and ReadOptionsBuilder.strictToon(boolean) setters.
    • ToonReader now enforces strict-mode validation (indentation, array counts, blank lines in arrays, delimiter/header consistency) when enabled.
    • Copy-constructor propagation and builder tests added.
  • TESTING: Added ToonSpecDecodeFixturesTest to run official toon-format/spec decode fixtures against JsonIo.fromToon() (opt-in via RUN_TOON_SPEC_FIXTURES=true).
  • PERFORMANCE: Read-side scalar coercion hot paths refactored to avoid legacy coerceLong()/coerceDouble() indirection:
    • Added target-kind based scalar coercion (scalarTargetKind / fastScalarCoercion) in Resolver.
    • ObjectResolver / MapResolver now use precomputed scalar target kinds in array and field hot loops.
    • Primitive array assignment now uses direct no-allocation writes via shared tryAssignParsedScalarToArray(...). This reduces wrapper churn and removes repeated coercion dispatch overhead in numeric-heavy deserialization.
  • PERFORMANCE: JsonWriter.writeMapBody(...) (both Iterator and JsonObject paths) now uses specialized loop branches with hoisted invariants (skipNullFields, json5UnquotedKeys, maxStringLength) to reduce per-entry branch checks in map write hot loops.
  • MAINTENANCE/PERFORMANCE: Inlined and removed now-redundant MapResolver.fastPrimitiveCoercion(...) wrapper after scalar coercion consolidation.
  • MAINTENANCE: Removed unused private method ToonWriter.writeCollectionElements(Collection<?>).
  • PERFORMANCE: JsonParser now uses a parser-only JsonObject.appendFieldForParser() fast path for object field insertion, avoiding JsonObject.put() duplicate-key search (indexOf) on the common unique-key path. This reduces parse-time key insertion overhead and shifts work from linear duplicate checks to direct append.
  • PERFORMANCE: Added thread-local buffer recycling in JsonIo String-based JSON paths:
    • toJson(Object, WriteOptions) now reuses FastByteArrayOutputStream and FastWriter buffers.
    • toJava(String, ...) and toJava(InputStream, ...) builder paths now reuse FastReader char/pushback buffers. This removes repeated stream/buffer construction churn in benchmark loops and reduces allocation pressure.
  • PERFORMANCE: Read numeric pipeline optimized:
    • JsonParser floating-point parsing now takes a direct Double.parseDouble() fast path when configured for DOUBLE mode, bypassing minimal-number analysis in the default path.
    • ObjectResolver.traverseArray() now has a primitive-array direct assignment path for parsed JSON Long/Double values, avoiding intermediate wrapper coercion in hot loops.
  • PERFORMANCE: JsonWriter custom-writer gate checks are now class-cached via shared DefaultWriteOptions caches (using ClassValueMap):
    • Added per-declared-class gate cache for isNotCustomWrittenClass and declared custom writer resolution.
    • Runtime custom writer resolution now reuses the existing getCustomWriter()/writerCache path after gate pass (no duplicate runtime cache layer). This lowers hot-loop cost in writeUsingCustomWriter() / getCustomWriterIfAllowed(), avoids per-JsonWriter cache construction churn in short-lived writer workloads, and removes redundant runtime cache indirection.
  • PERFORMANCE: JsonWriter now consistently honors cycleSupport(false) by bypassing objsReferenced / @id lookup work across write hot paths (object/map/collection/array/custom writer) while retaining lightweight visited-marker guarding to prevent infinite recursion on accidental cyclic input.
  • PERFORMANCE: Added cached write-field planning (WriteFieldPlan) in WriteOptionsBuilder and switched JsonWriter object/enum/trace field loops to use precomputed per-class metadata (serialized key literal, declared container generic types, and trace-skip hints), reducing repeated reflection/generic analysis in hot paths.
  • PERFORMANCE: Added cached read injector planning (InjectorPlan) in ReadOptionsBuilder and wired ObjectResolver/Resolver field lookup paths to use it, reducing repeated map resolution overhead during traversal and unresolved-reference patching.
  • PERFORMANCE: Accessor.retrieve() now uses sticky fallback flags for lambda/VarHandle/MethodHandle paths; once an optimized path fails, it is bypassed on subsequent calls instead of repeatedly throwing/falling back.
  • PERFORMANCE: Injector.inject() now applies assignability-based pre-conversion before reflective set/invoke, reducing exception-driven control flow in hot assignment paths while preserving fallback conversion behavior.
  • BUG FIX / PERFORMANCE: ObjectResolver generic inference now runs incrementally (on-demand) and no longer relies on the deep markUntypedObjects() pre-pass. This reduces upfront traversal work while preserving nested generic correctness across parameterized object fields, collections, and maps.
  • BUG FIX: ObjectResolver map generic typing now preserves and applies both key and value generic types during traversal, fixing cases where complex map keys/values could remain as JsonObject instead of resolving to target types.
  • CLEANUP: Removed legacy pre-pass code path and associated temporary API toggles (ReadOptions.useLegacyMarkUntypedObjectsPrepass() and ReadOptionsBuilder.useLegacyMarkUntypedObjectsPrepass(boolean)).
  • BUG FIX: JsonWriter.write() now always clears objVisited/objsReferenced in a finally block, preventing state leakage and object retention if write/flush throws.
  • BUG FIX: JsonWriter.traceReferences() object-limit handling now enforces limits without off-by-one behavior at the configured boundary.
  • PERFORMANCE: JsonWriter string escaping/writing hot paths optimized: removed String.format("\\u%04x", ...) from control-char escaping, added precomputed control escape strings, and unified smart-quote decision logic to avoid duplicated scanning code.
  • PERFORMANCE: JsonParser hot paths optimized with ASCII-only lowercase conversion for token reads, lookup-table hex parsing, bounded string dedupe caching, and simplified strict-mode whitespace/comment handling.
  • MAINTENANCE: Removed redundant strict-mode checks in skipWhitespaceRead() that were unreachable after strict-branch early return.
  • MAINTENANCE: Replaced resolver/read-path usage of Converter “simple type” APIs with internal Resolver.isPseudoPrimitive(Class<?>) gating (string-convertible, non-container, non-enum scalar-like types). This reduces dependency on confusing Converter simple-type API surface while preserving behavior in Resolver, ObjectResolver, and MapResolver.
  • MAINTENANCE: Refined Resolver.isPseudoPrimitive(Class<?>) to align with read-side semantics by checking isSimpleTypeConversionSupported(String.class, type) (materializable from JSON scalar strings), while still excluding enums.
  • MAINTENANCE: Simplified map pre-conversion decision logic in Resolver.toJava() by consolidating overlapping conditions into focused helpers (isMapLikeJsonType, shouldEarlyConvertMapToNonMap, resolveMapPojoValueTypeForPreConversion, shouldConvertMapValueTypeAsPojo) with parity behavior for map-to-POJO and parameterized map value materialization paths.
  • MAINTENANCE: ObjectResolver scalar fast-path conversion gates now use converter.isConversionSupportedFor(source, target) in both assignField() and readWithFactoryIfExists() (with existing scalar/simple checks retained), reducing dependence on the pair-form "simple type" API without changing behavior.
  • TESTING: Added ReadWithFactoryDeadCodeTest.testReadWithFactoryIfExists_primitiveStringMismatchUsesConversionSupportGate() to verify the primitive/String mismatch fast path uses conversion support checks and preserves null-return fallback for unsupported scalar targets.
  • BUG FIX: ObjectResolver.processJsonObjectElement() now preserves explicit element @type metadata in arrays by only applying declared component type when the element type is missing. This fixes polymorphic array deserialization where explicit subtype entries could be overwritten by the declared array component type.
  • TESTING: Added PolymorphicArrayElementTypeTest to verify explicit array element @type is honored over declared component type inference.
  • BUG FIX: Resolver.wrapException() now preserves the causal chain by constructing JsonIoException(message, cause) instead of creating a message-only wrapper. This restores root exception context for conversion failures.
  • TESTING: Added ResolverValueToTargetTest.conversionFailurePreservesCause() to assert conversion failure wrappers retain the original cause.
  • MAINTENANCE/PERFORMANCE: ObjectResolver nested collection type-marking path was cleaned up by removing an unused collectionClass parameter and eliminating per-nested-array ArrayList copy allocation in handleArrayInCollection(). Nested arrays now reuse the existing JsonObject wrapper for traversal, reducing allocation churn in deep generic collection payloads.
  • MAINTENANCE: Version bump to 4.94.0, java-util dependency updated to 4.94.0.

4.93.0 - 2026-02-10

  • PERFORMANCE: JsonWriter - Localized this.out to stack variable in 8 hot-path methods (writeKey, writeField, writeObject, writePrimitive, writeLongDirect, writeIntDirect, writeIntArray, writeShortArray, writeIdAndTypeIfNeeded) for consistent register allocation, matching the pattern already used by 14 other methods in the class.
  • MAINTENANCE: Injector/Accessor - Reordered creation paths to prefer privateLookupIn-based LambdaMetafactory first (no setAccessible needed), demoting setAccessible(true) to a legacy fallback. Prepares for Java 25+ which may further restrict reflective access. No behavioral change — runtime dispatch order unchanged.
  • MAINTENANCE: Version bump to 4.93.0, java-util dependency updated to 4.93.0.

4.92.0 - 2026-02-08

  • PERFORMANCE: Optimized Resolver/ObjectResolver read path (~6% faster Java deserialization)
    • Correctness fix: createSameTypeCollection() - Fixed inheritance-order bug where LinkedHashSet was incorrectly created as HashSet, losing insertion-order preservation
    • Correctness fix: shouldSkipTraversal() - Fixed rawClass.isInstance(Number.class) (always false) to Number.class.isAssignableFrom(rawClass)
    • Performance: Eliminated strategy pattern in assignField() - removed AssignmentStrategy interface, AssignmentContext class, and 7 strategy methods; inlined as direct if/else chain, eliminating ~16M object allocations per 100k iterations
    • Performance: Added fast primitive coercion to ObjectResolver (traverseArray, traverseCollection, assignField) - direct Long→int/short/byte and Double→float casts bypass expensive Converter lookup chains
    • Performance: Injector now uses LambdaMetafactory-generated BiConsumer for field injection (~5-8% faster Read). The JIT can inline the generated lambda to near-direct field access speed, replacing non-inlinable MethodHandle/VarHandle instance-field dispatch. Uses privateLookupIn (JDK 9+) for non-public classes; falls back gracefully to existing mechanisms.
    • Performance: Accessor now uses LambdaMetafactory-generated Function for field access during JSON writing. Same JIT-inlinable technique as Injector, applied to the write path.
  • BUG FIX: JsonWriter.doesValueTypeMatchFieldType() - declaredClassIsLongWrittenAsString incorrectly checked objectClass instead of declaredType for the long.class case, causing unnecessary @type wrappers on primitive long fields when writeLongsAsStrings was enabled
  • BUG FIX: JsonWriter.doesValueTypeMatchFieldType() - Long.class vs long.class autoboxing mismatch caused unnecessary @type wrappers on Long values assigned to primitive long fields
  • BUG FIX: JsonWriter - Removed JSON5 trailing comma writes; trailing commas are now supported on READ (JSON5 tolerance) but never written, as they added complexity without benefit
  • BUG FIX: ToonWriter/ToonReader - Fixed empty map handling: ToonWriter now writes empty maps inline as {} in both standalone and list-element contexts; ToonReader now recognizes {} in list arrays as empty objects instead of treating them as strings
  • BUG FIX: ToonReader.parseNumber() - Replaced custom numeric parsing with delegation to MathUtilities.parseToMinimalNumericType() from java-util, fixing extreme doubles (e.g., Double.MAX_VALUE written as 309-digit plain integer) and large BigInteger values that were incorrectly returned as strings
  • BUG FIX: ToonWriter - Fixed double-colon bug in nested collections: writeCollection() was writing [N]: then calling writeCollectionElements() which added {cols}: for tabular format, producing [N]:{cols}: instead of [N]{cols}:
  • BUG FIX: ToonReader.isArrayStart() - Fixed tabular array detection: the method checked for ]: as a contiguous substring but tabular format [N]{cols}: has ]{ between ] and :. Now correctly checks the character after ] for either : or {
  • PERFORMANCE: JsonWriter - Pre-fetched custom writers for primitive array hot paths (long[], double[], float[]), avoiding per-array getCustomWriter() lookups
  • PERFORMANCE: JsonWriter - Added writeIntDirect() for zero-allocation int/short writing using digit-pair lookup tables, replacing Integer.toString() + Writer.write(String)
  • PERFORMANCE: JsonWriter - Cached EnumSet.elementType field reflectively (lazy-initialized volatile) to avoid repeated getDeepDeclaredFields() lookups
  • PERFORMANCE: ObjectResolver.coerceLong() - Reordered branches by frequency (int/double first, then byte/float/short)
  • FEATURE: TOON key folding support (optional, spec-compliant)
    • Writer: Added WriteOptionsBuilder.toonKeyFolding(boolean) to collapse single-key object chains into dotted notation
      • {data: {metadata: {value: 42}}}data.metadata.value: 42
      • Works with arrays: data.metadata.items[3]: a,b,c
    • Reader: Always expands dotted keys into nested structures (unless quoted)
      • config.database.host: localhost{config: {database: {host: localhost}}}
      • Quoted keys preserved as literals: "dotted.key": value{dotted.key: value}
  • FEATURE: TOON tabular format delimiter variants support
    • Added support for tab-separated tabular format: items[2\t]{col1\tcol2}: with tab-delimited rows
    • Added support for pipe-separated tabular format: items[2|]{col1|col2}: with pipe-delimited rows
    • Comma delimiter remains the default: items[2]{col1,col2}:
    • All three variants parse to identical data structures and round-trip correctly
  • TESTING: Comprehensive TOON test suite — 241 tests total across ToonReaderTest and ToonWriterTest
    • 30 high-priority spec compliance tests (scalars, arrays, objects, nesting, round-trips)
    • 43 medium/lower priority tests (special types, edge cases, quoting rules, format variants)
    • 9 heterogeneous depth chain tests (all structural type combinations at every nesting level)
    • 31 gap tests: blank lines in tabular/list/object data, odd indentation, array count mismatches, fromToonToMaps() with tabular arrays, unclosed quotes/malformed input resilience, invalid escape sequence error handling
  • MAINTENANCE: Version 4.92.0, java-util dependency updated to 4.92.0

4.91.0 - not released

4.90.0 - 2026-02-02

  • MAINTENANCE: Migrated test files from deprecated JsonIo.toObjects() to JsonIo.toJava().asClass() API
    • Updated ~148 calls across 5 test files to use the new fluent builder pattern
    • Deprecated toObjects() methods remain available in JsonIo.java for backward compatibility
  • REFACTOR: Consolidated duplicate parse/resolve logic in JsonIo builder classes
    • Extracted common parseAndResolve() helper method using functional interface pattern
    • Unified error handling, unsafe mode management, and cleanup across all 5 builders
    • Reduced ~150 lines of duplicated code to ~35 lines of shared infrastructure
  • PERFORMANCE: Optimized JsonParser.readString() with bulk character reading
    • Uses new FastReader.readUntil() for bulk reads until quote or backslash
    • Reduces per-character I/O overhead with 256-char buffer reads
    • Bulk appends to StringBuilder instead of character-by-character
  • PERFORMANCE: Optimized JsonWriter.writeCustom() to avoid redundant getCustomWriter lookups
    • Combined isCustomWrittenClass check with writer lookup into single getCustomWriterIfAllowed method
    • When declared type equals runtime type (common case), only one getCustomWriter call instead of two
  • PERFORMANCE: Optimized JsonWriter.writeJsonUtf8String() with escape lookup table
    • Pre-computed ESCAPE_STRINGS[128] array: null = no escape, non-null = the escape string
    • Single array lookup replaces 4 comparisons + switch statement
    • Also escapes Unicode line/paragraph separators (U+2028/U+2029) for JavaScript compatibility

4.89.0 - 2026-01-31

  • SPRING: Default show-type-info changed from MINIMAL to MINIMAL_PLUS
    • Spring Boot starter now uses MINIMAL_PLUS as the default for optimal JSON size
    • Added MINIMAL_PLUS option to Spring configuration properties
  • IMPROVED: Numeric primitive simplification in MINIMAL_PLUS and NEVER type info modes
    • Byte, Short, Integer, and Float values now write as plain JSON numbers when the declared type is Object
    • Applies to fields declared as Object, collection elements in raw collections, and Map<String, Object> values
    • These types round-trip as Long (for integers) or Double (for floats) when read back
    • Reduces JSON size by eliminating {"@type":"Integer","value":42} wrappers in favor of plain 42
    • Does not apply to MINIMAL or ALWAYS modes to maintain backward compatibility

4.88.0 - 2026-01-26

  • IMPROVED: MapResolver - Applied consistency improvements from ObjectResolver patterns
    • Added isFinished guard to traverseMap() to prevent reprocessing
    • Updated traverseArray() and traverseCollection() to use markFinishedIfNot() helper
    • Removed redundant setFinished() calls at method ends (now handled by guard pattern)
  • BUILD: Fixed json-io-spring-boot-starter Maven Central deployment
    • Added required deployment plugins (maven-source-plugin, maven-javadoc-plugin, central-publishing-maven-plugin)
    • Spring Boot starter now properly publishes to Maven Central alongside json-io core
  • BUILD: Updated java-util dependency to 4.89.0

4.87.0 - 2025-01-26

  • FEATURE: Spring Boot Starter module (json-io-spring-boot-starter)
    • New Maven artifact for seamless Spring Boot 3.x integration
    • Auto-configuration for Spring MVC with HttpMessageConverter support
    • Spring MVC HttpMessageConverters for content negotiation:
      • JsonIoHttpMessageConverter - application/json
      • Json5HttpMessageConverter - application/vnd.json5
      • ToonHttpMessageConverter - application/vnd.toon
    • WebFlux Encoders/Decoders for reactive applications:
      • JsonIoEncoder / JsonIoDecoder - application/json
      • Json5Encoder / Json5Decoder - application/vnd.json5
      • ToonEncoder / ToonDecoder - application/vnd.toon
    • Configuration properties under spring.json-io.*:
      • Write options: pretty-print, show-type-info, skip-null-fields, short-meta-keys, etc.
      • Read options: max-depth, fail-on-unknown-type, allow-nan-and-infinity
      • Integration: jackson-mode (COEXIST, REPLACE, JSON5_ONLY)
    • Customizer interfaces for programmatic configuration:
      • ReadOptionsCustomizer - Customize read behavior
      • WriteOptionsCustomizer - Customize write behavior
    • Jackson coexistence modes:
      • COEXIST (default) - json-io handles JSON5/TOON, Jackson handles JSON
      • REPLACE - json-io handles all formats
    • See Spring Integration Guide for details
  • BUILD: Converted to multi-module Maven project structure
    • json-io - Core library (unchanged artifact coordinates)
    • json-io-spring-boot-starter - Spring Boot integration module

4.85.0 - 2025-01-24

  • FEATURE: TOON (Token-Oriented Object Notation) output support
    • Added JsonIo.toToon(Object, WriteOptions) - Convert Java objects to TOON format string
    • Added JsonIo.toToon(OutputStream, Object, WriteOptions) - Stream TOON output directly
    • TOON is a compact, human-readable format optimized for LLM token efficiency (~40-50% fewer tokens than JSON)
    • Key TOON characteristics:
      • Indentation-based structure (2 spaces, LF line endings) - no braces/brackets
      • Primitive arrays inline: tags[3]: foo,bar,baz
      • Mixed arrays with hyphen list format
      • Key: value object syntax
      • Minimal quoting (only when necessary per TOON spec)
      • Only 5 escape sequences: \\, \", \n, \r, \t
      • NaN/Infinity → null, -0 → 0 normalization per spec
    • Cycle detection included (silently skips cyclic references)
    • Uses cached reflection via WriteOptions.getDeepDeclaredFields() for efficient field discovery
    • Respects WriteOptions excluded/included field settings
    • See TOON Format Specification for details
    • Comprehensive type support for serialization:
      • Primitives and wrappers: Boolean, Byte, Short, Integer, Long, Float, Double, Character
      • Big numbers: BigInteger, BigDecimal
      • Atomic types: AtomicBoolean, AtomicInteger, AtomicLong
      • String types: String, StringBuilder, StringBuffer
      • Date/Time: Date, Timestamp, Calendar, Instant, LocalDate, LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, OffsetTime, Year, YearMonth, MonthDay, Duration, Period
      • Zones: ZoneId, ZoneOffset, TimeZone
      • IDs: UUID, URI, URL
      • Files: File, Path
      • Other: Locale, Currency, Class, Pattern, BitSet (as binary string), Enum
      • Collections: Collection, List, Set, Map, arrays
  • FEATURE: TOON (Token-Oriented Object Notation) input support
    • Added JsonIo.fromToon(String, ReadOptions) - Parse TOON format string to Java objects
    • Added JsonIo.fromToon(InputStream, ReadOptions) - Stream TOON input directly
    • Added JsonIo.fromToonToMaps(String, ReadOptions) - Parse TOON to Maps (class-independent)
    • Added JsonIo.fromToonToMaps(InputStream, ReadOptions) - Stream TOON to Maps
    • Fluent builder API:
      • .asClass(Class<T>) - Parse and convert to specific class
      • .asType(TypeHolder<T>) - Parse and convert with generic type information
    • Produces same JsonObject structures as JsonParser, reusing existing Resolver infrastructure
    • Full TOON format support including:
      • Indentation-based structure (2 spaces per level)
      • key: value object syntax with nested structure detection
      • Inline arrays: [N]: elem1,elem2,elem3
      • List format arrays: [N]: followed by - elem lines
      • Scalar parsing: null, true, false, numbers (Long/Double), strings
      • Quoted strings with 5 escape sequences: \\, \", \n, \r, \t
    • Generic type support via TypeHolder:
      • List<Person> - Collection elements converted to target type
      • Map<String, Person> - Map values converted to target type
      • Map<Person, String> - Maps with complex object keys (uses $key/$value array-of-entries format)
  • FEATURE: WriteOptionsBuilder - Added cycleSupport(boolean) option for performance optimization
    • cycleSupport(true) (default) - Full cycle support with @id/@ref for multi-referenced objects
    • cycleSupport(false) - Skips the traceReferences() pre-pass for faster serialization of acyclic data
    • When cycleSupport=false and a cycle is detected during writing, duplicate references are silently skipped (no infinite loop, no exception)
    • Performance benefit: ~35-40% faster writes for graphs without cycles
    • Comparison to other libraries:
      • Jackson: Requires @JsonIdentityInfo annotation, throws exception if cycles not handled
      • GSON: No built-in cycle support, throws StackOverflowError on cycles
      • json-io with cycleSupport=true: Automatic cycle handling, no annotations needed
      • json-io with cycleSupport=false: Skip overhead when cycles are known to not exist
    • Added isCycleSupport() getter to WriteOptions
    • Added addPermanentCycleSupport(boolean) for application-scoped configuration
  • BUILD: Updated java-util dependency from 4.84.0 to 4.85.0

4.84.0 - 2025-01-19

  • FEATURE: WriteOptionsBuilder - Added meta key prefix override methods
    • useMetaPrefixAt() - Forces @ prefix for all meta keys (@type, @id, @ref, etc.) even in JSON5 mode
    • useMetaPrefixDollar() - Forces $ prefix for all meta keys ($type, $id, $ref, etc.) even in standard JSON mode
    • getMetaPrefixOverride() - Returns the override character (@, $, or null for default behavior)
    • Default behavior: Standard JSON uses @ prefix (quoted), JSON5 uses $ prefix (unquoted)
    • In JSON5, @ cannot be used unquoted since it's not a valid ECMAScript identifier start character
    • Reading accepts all meta key variants (@type, @t, $type, $t) regardless of format used to write

4.83.0 - 2025-01-18

  • BUG FIX: JsonWriter.writeJsonObjectObject() - Fixed JSON5 unquoted keys not being applied
    • When serializing JsonObject instances directly, the json5UnquotedKeys option was ignored
    • Field names were always written with quotes regardless of JSON5 settings
    • Now uses consistent key writing logic with writeMapBody() - unquoted for valid ECMAScript identifiers
    • Also fixes potential escaping issues for field names containing special characters
  • PERFORMANCE: JsonWriter - Comprehensive hot path optimization
    • Hoisted 12 WriteOptions values to final member variables initialized at construction time
    • Pre-fetched: skipNullFields, json5TrailingCommas, json5UnquotedKeys, maxStringLength, prettyPrint, neverShowingType, alwaysShowingType, writeLongsAsStrings, json5SmartQuotes, maxIndentationDepth, indentationThreshold, indentationSize
    • Eliminated ~37 method calls per serialization cycle across all write paths
    • Restructured writeMapBody() and writeElements() loops to write comma BEFORE entries (except first), eliminating double hasNext() check per iteration
    • Replaced all internal getWriteOptions() calls with direct field access
    • Optimized tab() method to use pre-fetched indentation settings
  • PERFORMANCE: JsonParser - Zero-allocation string cache on cache hits
    • Replaced LinkedHashMap-based cache with array-based open addressing (2048 slots)
    • Computes hashCode directly from CharSequence without creating String
    • Uses contentEquals() to verify matches - only allocates String on cache miss
    • ~9% improvement in read performance
  • PERFORMANCE: JsonObject.indexOf() - Reduced linear search overhead
    • Lowered INDEX_THRESHOLD from 16 to 4 for earlier HashMap index creation
    • Added proactive index building when crossing threshold during put()
    • Reduces O(n²) cost when building objects with many fields
  • PERFORMANCE: WriteOptionsBuilder / ReadOptionsBuilder - Eliminated lambda allocations
    • Replaced computeIfAbsent(key, this::method) with manual get-then-put pattern
    • Method references create new lambda instances on every call, even on cache hits
    • Fixed in 5 locations: getAccessorsForClass, getCustomWriter, getDeepDeclaredFields, getDeepInjectorMap
  • PERFORMANCE: JsonWriter - New IdentityIntMap for reference tracking
    • Replaced IdentityHashMap<Object, Long> with lightweight IdentityIntMap
    • Uses open addressing with primitive int values (no Entry objects, no boxing)
    • Single System.identityHashCode() call per operation
    • ~10% improvement in write performance
  • PERFORMANCE: ObjectResolver.markUntypedObjects() - Optimized visited set
    • Now uses IdentitySet from java-util (high-performance Set using object identity)
    • Previously used workaround with IdentityIntMap, now uses proper add()/contains() semantics
    • 66% reduction in samples for this method, eliminated IdentityHashMap allocations
  • BUG FIX: ReadOptionsBuilder.integerTypeBigInteger() - Fixed to actually set BigInteger-only mode
    • Method was incorrectly setting Integers.BOTH instead of Integers.BIG_INTEGER
    • Now correctly forces all integers to return as BigInteger regardless of size
  • TESTING: Added comprehensive JsonParserErrorHandlingTest with 52 tests for parser edge cases
    • @items validation errors, invalid field name starts, token EOF/mismatch errors
    • BigInteger mode and overflow handling, hex number overflow
    • Unicode escape errors, surrogate pair handling (valid, invalid, orphan)
    • Multi-line string validation in strict JSON mode
    • Root-level string trailing content validation
    • @id, @ref, @enum validation (invalid types, out-of-range values)
  • BUG FIX: JsonParser.readNumberGeneral() - Fixed EOF handling after '+' sign in JSON5 positive numbers
    • When parsing [+ (plus sign followed by EOF), the parser would push back EOF (-1) as character \uFFFF
    • This corrupted the stream state and prevented the proper "Unexpected end of input after '+'" error
    • Added check to only push back valid characters, not EOF
  • PERFORMANCE: JsonParser.readJsonObject() - Skip type resolution for simple Class types
    • When suggestedType is null or already a Class, skip the redundant TypeUtilities.resolveType() call
    • Only ParameterizedType and other complex types need resolution against themselves
    • This is the most common case in parsing, reducing overhead in the hot path
  • BUG FIX: ObjectResolver - Fixed deserialization of arrays containing Collections
    • Fields declared as List<String>[], ArrayList<Integer>[], etc. now deserialize correctly
    • Previously, resolveArray() incorrectly called createAndPopulateCollection() for Collection element types, creating a single Collection instead of an array of Collections
    • Added handling in createAndPopulateArray() to convert inner Object[] to Collection instances when the component type is a Collection
    • Also fixed assignField() to use instanceof Object[] check instead of isArray() to avoid ClassCastException with primitive arrays
    • Removed unused createAndPopulateCollection() method
  • PERFORMANCE: JsonParser - Optimized string caching and removed number caching
    • String cache: Simplified to bounded LRU LinkedHashMap (1024 entries) with automatic eviction
    • Number cache: Removed entirely - was boxing primitives before cache lookup, negating any benefit
    • Small integers (-128 to 127) use JVM's built-in Long.valueOf() cache
    • Eliminates autoboxing overhead from cache lookups on every number parsed
  • PERFORMANCE: ObjectResolver - Direct array resolution to avoid double allocation
    • Previously resolveArray() created both a temporary items array and the target array
    • New createAndPopulateArray() method creates and populates the typed array directly
    • Handles primitive arrays, Object arrays, nested arrays, and forward references
    • Collection resolution also optimized with direct population
  • REFACTOR: ObjectResolver - Extracted shared array element processing helpers
    • Eliminated code duplication between traverseArray() and createAndPopulateArray()
    • Created 6 shared helper methods: resolveReferenceElement(), processJsonObjectElement(), convertToComponentType(), handleNestedArrayElement(), handleCharArrayElement(), handleCollectionElement()
    • Both methods now use the same logic for reference resolution, type conversion, and nested structure handling
    • No behavioral changes - purely internal refactoring for maintainability
  • TESTING: Added MapResolverGenericArrayTest with 13 tests for MapResolver coverage
    • GenericArrayType handling in getUltimateComponentType() - Tests parsing of generic array types like List<String>[] in Maps mode
    • reconcileResult() method coverage - Tests for returning simple types (String, Long, Double, Boolean) directly and returning JsonObject for complex types
    • Edge cases: empty generic arrays, generic arrays with null elements, JSON null handling

4.82.0 (unreleased)

  • REMOVED: ReadOptions.getMaxEnumNameLength() and related builder methods
    • Removed maxEnumNameLength security limit option - enum name length is already bounded by Java class loading
    • Removed ReadOptionsBuilder.maxEnumNameLength() and addPermanentMaxEnumNameLength() methods
    • Simplifies the API by removing unnecessary security option
  • PERFORMANCE: Resolver - Hoisted additional ReadOptions constants
    • Pre-fetched maxStackDepth, maxMapsToRehash, and returningJavaObjects to final fields
    • Eliminates repeated method calls in hot paths during JSON parsing

4.81.0 - 2025-01-10

BUG FIX: Resolver - Fixed array/collection cross-conversion returning null

  • When JSON contained @type=char[] but caller requested byte[].class, json-io returned null instead of converting
  • Same issue affected other array cross-conversions (e.g., byte[]char[], int[]long[]) and array ↔ collection conversions
  • Added early conversion logic in toJava() that detects type mismatches and uses the Converter to transform between array/collection types
  • Now properly handles: array → different array, array → Collection, Collection → array
  • Related fix in java-util's Converter (see java-util 4.81.0 changelog)
  • FEATURE: Added omitRootTypeInfo() and showRootTypeInfo() to WriteOptionsBuilder
    • Control whether @type is written on the root object when using showTypeInfoMinimal() (the default)
    • omitRootTypeInfo() - Omit the @type on the root object, useful when the reader uses .asClass() or .asType()
    • showRootTypeInfo() - Explicitly show the @type on the root object (current default)
    • Added isShowingRootTypeInfo() getter to WriteOptions
    • Validation: These methods are only valid with showTypeInfoMinimal(). Using them with showTypeInfoAlways() or showTypeInfoNever() throws IllegalStateException — those modes have absolute behavior that cannot be overridden
    • Current default is to show root type (backward compatible), but this will likely change to omit in a future release
    • This allows reducing JSON payload size when the receiving system knows the expected type

4.80.0 - 2025-01-05

  • FEATURE: JSON5 Support - Parser now accepts JSON5 extensions by default
    • Added ReadOptionsBuilder.strictJson() for RFC 8259 compliance mode
    • Default is permissive mode (accepts JSON5 features)
    • When strictJson() is enabled, JSON5 extensions cause parse errors
    • Unquoted object keys: Object keys can now be valid ECMAScript identifiers without quotes
      • Keys must start with a-z, A-Z, underscore (_), or dollar sign ($)
      • Subsequent characters can include digits (0-9)
      • Examples: {name:"John"}, {_private:1}, {$jquery:"lib"}
    • Comments: Both single-line and block comments are now supported
      • Single-line comments: // comment until end of line
      • Block comments: /* comment */ (can span multiple lines)
      • Comments can appear anywhere whitespace is allowed
    • Trailing commas: Objects and arrays can now have a trailing comma
      • Objects: {"a": 1, "b": 2,} is valid
      • Arrays: [1, 2, 3,] is valid
      • Nested structures with trailing commas: {"arr": [1, 2,], "name": "test",}
    • Single-quoted strings: Strings can use single quotes instead of double quotes
      • Values: {"name": 'John'} is valid
      • Keys: {'name': "John"} is valid
      • Single quotes can contain unescaped double quotes: {'text': 'He said "Hello"'}
      • Escape single quotes with backslash: {'text': 'It\'s working'}
    • Hexadecimal numbers: Integer literals can be specified in hexadecimal
      • Lowercase: {"value": 0xff} equals 255
      • Uppercase: {"value": 0xFF} equals 255
      • Negative hex: {"value": -0xFF} equals -255
      • Up to 16 hex digits supported (full 64-bit range)
    • Special number formats: JSON5 number format extensions
      • Leading decimal point: {"value": .5} equals 0.5
      • Trailing decimal point: {"value": 5.} equals 5.0
      • Explicit positive sign: {"value": +5} equals 5
      • Combinations supported: +.5 (0.5), -.5 (-0.5), +5. (5.0)
      • Works with exponents: .5e2 (50.0), 5.e2 (500.0), +1e5 (100000.0)
    • Multi-line strings: Strings can span multiple lines using backslash continuation
      • Backslash followed by LF (\↵) removes both characters
      • Backslash followed by CR (\␍) removes both characters
      • Backslash followed by CRLF (\␍↵) removes all three characters
      • Leading whitespace on continuation lines is preserved
      • Example: "hello \↵world" becomes "hello world"
  • FEATURE: JSON5 Write Support - WriteOptionsBuilder now supports JSON5 output options
    • Added json5() umbrella method to enable recommended JSON5 writing features
    • Added json5UnquotedKeys(true) to write object keys without quotes when valid identifiers
      • Keys must be valid ECMAScript identifiers (start with letter//$, contain letters/digits//$)
      • Keys with special characters, spaces, or starting with digits are still quoted
      • Example: {name:"John", "key-with-dash":"value"}
    • Added json5SmartQuotes(true) for adaptive quote selection on string values
      • Uses single quotes if string contains " but no ' (cleaner output for strings with embedded quotes)
      • Uses double quotes otherwise (standard behavior)
      • Example: {message:'He said "Hello"'} instead of {message:"He said \"Hello\""}
    • Added json5InfinityNaN(true) to write Infinity/NaN as literals instead of null
      • Double.POSITIVE_INFINITY writes as Infinity
      • Double.NEGATIVE_INFINITY writes as -Infinity
      • Double.NaN writes as NaN
      • Same for Float values
      • Works with legacy allowNanAndInfinity(true) option
    • Added json5TrailingCommas(true) to write trailing commas after last element
      • Arrays: [1, 2, 3,] - trailing comma after last element
      • Objects: {name:"John", age:30,} - trailing comma after last field
      • Not enabled by json5() umbrella - requires explicit opt-in
      • Useful for cleaner diffs and easier editing when format allows
    • JSON5 meta key prefixes use $ instead of @ for unquoted keys
      • $type, $id, $ref, $items, $keys in JSON5 mode
      • When combined with shortMetaKeys(true): $t, $i, $r, $e, $k
      • All variants are accepted when reading: @type, @t, $type, $t
  • API: JsonValue.getReferenceId() now returns primitive long instead of Long
    • Previously returned Long (null if not a reference)
    • Now returns long (0 if not a reference, since IDs start at 1)
    • Use isReference() to check before calling getReferenceId()
    • Migration: Change Long ref = obj.getReferenceId(); if (ref != null) to if (obj.isReference()) { long ref = obj.getReferenceId(); ... }
  • API: JsonValue.setReferenceId() now takes primitive long instead of Long
    • Previously accepted Long (null to clear)
    • Now accepts long (use 0 to indicate not a reference)
    • Migration: Change setReferenceId(null) to setReferenceId(0)
  • PERFORMANCE: JsonWriter - Optimized traceReferences() to reduce object allocation
    • Replaced single Deque<Object[]> with Deque<Object> plus primitive int[] for depths
    • Eliminates new Object[]{element, depth} allocation for every object pushed during reference tracing
    • Eliminates Integer autoboxing by using primitive int[] with automatic growth
    • Reduces GC pressure when serializing large object graphs
  • PERFORMANCE: WriteOptionsBuilder - Cache isNonReferenceableClass() result using ClassValue
    • For POJOs (not in nonRefClasses), previously performed 4 hierarchy/type checks on every call
    • Now caches the boolean result per-class using JVM-optimized ClassValue
    • Particularly beneficial since this method is called twice per object (trace + write phases)
  • PERFORMANCE: JsonWriter - Type-indexed dispatch in writeImpl() using ClassValue
    • Replaced cascade of 6 instanceof checks with cached WriteType enum lookup
    • For POJOs (the common case), avoids all instanceof checks after first encounter
    • Uses switch on cached enum for efficient dispatch to write methods
  • PERFORMANCE: JsonWriter - Write @id and @ref values directly without String allocation
    • Changed writeId() to take long instead of String, eliminating Long.toString() calls
    • Added writeLongDirect() method using digit-pair lookup tables for fast long-to-chars conversion
    • Uses reusable scratch buffer instead of creating String objects for each ID
  • PERFORMANCE: JsonParser - Optimized skipWhitespaceRead() with direct character comparison
    • Replaced array bounds check + lookup (c >= 0 && c < 128 && WHITESPACE[c]) with direct comparison
    • Now uses c == ' ' || c == '\t' || c == '\n' || c == '\r' which is faster
    • Removed unused WHITESPACE_MAP array
    • ~4% read improvement in Maps mode, ~1.5% in Java mode
  • PERFORMANCE: JsonParser - Extended number parsing fast path to handle -99 to 99
    • Previously only single positive digits (0-9) used fast path
    • Now handles: single digits, two-digit numbers, negative single/two-digit numbers
    • Avoids StringBuilder allocation for ~80% of integers in typical JSON
    • ~9% read improvement in Maps mode (3.81x → 3.47x vs Jackson)
  • PERFORMANCE: JsonObject - Added cached JsonType enum for type dispatch optimization
    • Added getJsonType() method that caches result of isArray()/isCollection()/isMap() checks
    • Resolver.traverseSpecificType() and JsonWriter.writeImpl() now use cached type dispatch
    • Avoids repeated type classification checks during object graph traversal
  • PERFORMANCE: JsonParser - Simplified number parsing with direct StringBuilder-to-long conversion
    • Parse integers directly from StringBuilder.charAt() without String allocation
    • Optimization from the original heap-based parser avoids both String.toString() and Long.parseLong() overhead
    • Removed 80+ lines of complex 1-2 digit special-case branching code
    • Single general path now handles all integer sizes (up to 18 digits parsed directly, 19+ fall back to String)
    • ~2-3% read improvement in Maps mode
  • PERFORMANCE: JsonParser - BMP fast-path for Unicode escape sequences
    • Most Unicode escapes are BMP characters (Basic Multilingual Plane, U+0000 to U+FFFF excluding surrogates)
    • Added early check for non-surrogate values before surrogate pair handling logic
    • Skips surrogate detection branches for ~99% of Unicode escapes in typical JSON
  • PERFORMANCE: JsonParser - Hoist ReadOptions constants to class fields
    • Hoisted strictJson, integerTypeBigInteger, integerTypeBoth, floatingPointBigDecimal, floatingPointBoth
    • Previously called readOptions.isXxx() methods repeatedly in hot parsing loops
    • Now cached as final fields at parser construction time
  • PERFORMANCE: Accessor - VarHandle fallback for JDK 17+ module system compatibility
    • When MethodHandle fails due to module restrictions, falls back to VarHandle on JDK 17+
    • VarHandle can access fields even when setAccessible() is blocked by the module system
    • Maintains getMethodHandle() API compatibility by trying MethodHandle first
  • PERFORMANCE: Accessor.retrieve() - Remove redundant type check from hot path
    • Removed field.getDeclaringClass() + isInstance() check on every field retrieval
    • The underlying MethodHandle/VarHandle/Field.get() will throw appropriate exceptions if given wrong type
    • Moved getDeclaringClass() call to exception handling path only (rarely executed)
  • PERFORMANCE: Injector - Remove deprecated SecurityManager checks
    • SecurityManager was deprecated in JDK 17 and removed in JDK 24
    • Removed all System.getSecurityManager() checks that added overhead on every injection
    • Removed unused isSystemClass field and ReflectPermission import
  • PERFORMANCE: IsMethodAccessorFactory - Optimized createIsName() string creation
    • Replaced String concatenation with direct char[] construction
    • Eliminates intermediate String allocations for boolean accessor method names
  • PERFORMANCE: MethodInjectorFactory - Optimized createSetterName() string creation
    • Replaced String concatenation with direct char[] construction
    • Eliminates intermediate String allocations for setter method names
  • PERFORMANCE: JsonObject - Eliminate redundant data storage and object allocations
    • Removed CacheState inner class - eliminated 1 object allocation per JsonObject
    • Replaced cached keySet() copy with lightweight ArrayKeySet view class (no data copying)
    • Replaced cached values() copy with lightweight ArrayValueCollection view class (no data copying)
    • Removed unnecessary caching of keys.length and items.length (already O(1) array access)
    • Use primitive byte sortedCache instead of boxed Boolean for sorted state
  • PERFORMANCE: Resolver - Early exit for empty post-parse collections
    • patchUnresolvedReferences() now returns immediately if no forward references to patch
    • rehashMaps() now returns immediately if no maps need rehashing
    • Avoids iteration overhead when Parser resolved everything during parsing
  • MEMORY: JsonValue - Reduced memory footprint by 16 bytes per object
    • Removed line and col fields (8 bytes) - FastReader already removed tracking, these were always 0
    • Changed id from long to int (4 bytes) - 2.1 billion unique IDs is sufficient
    • Changed refId from Long to int with -1 sentinel (4+ bytes) - avoids object allocation
    • Public API unchanged for backward compatibility (getId() returns long, getReferenceId() returns Long)
    • Error messages now use snippet context instead of useless "line 0, col 0"
  • CLEANUP: JsonReader - Removed all implementation code, retaining only deprecated inner interfaces for backward compatibility
    • JsonReader class is now marked @Deprecated - use JsonIo for all JSON parsing
    • Private constructor prevents instantiation with clear error message
    • Deprecated inner interfaces retained for source compatibility:
      • JsonReader.ClassFactory → use top-level ClassFactory
      • JsonReader.MissingFieldHandler → use top-level MissingFieldHandler
      • JsonReader.JsonClassReader → use top-level JsonClassReader
    • Reduces class from 873 lines to 74 lines
    • All functionality now handled by JsonIo, JsonParser, and Resolver
    • Merged PRs #427 and #428 from @Shiyang-Zhao to correct non-deterministic tests

4.72.0 - 2025-12-31

  • DEPENDENCY: Updated java-util to version 4.72.0
    • Includes fix for ThreadedLRUCacheStrategy scheduled task accumulation
    • Includes fix for Jackson dependencies incorrectly declared without <scope>test</scope>
  • PERFORMANCE: JsonWriter - Eliminate redundant @type for Collection and Map elements
    • When a field is declared with generic type info (e.g., List<Person>), @type is now omitted on elements when the element class exactly matches the declared element type
    • Extends to Map keys/values when using @keys/@items format (e.g., Map<Building, Person>)
    • Produces shorter JSON output without loss of type information
    • Parser already handles type inference from context, so this is backward compatible
  • PERFORMANCE: MapResolver - Optimized Maps mode from 9x slower to 4x slower than Jackson
    • Added MAP_OPTIONS_CACHE to avoid creating ReadOptionsBuilder on every toMaps() call
    • Added fastPrimitiveCoercion() for common JSON primitive to Java primitive conversions without Converter lookup
    • Added isNonReferenceableClass check before Converter lookup in traverseArray - user types are not nonRef and Converter cannot convert them
    • Added early exit for Object.class or matching types in traverseFields
    • Cache readOptions local variable in traverseArray hot loop
  • PERFORMANCE: Resolver - Added early isFinished check in push() method
    • Skips pushing objects that are already fully resolved, reducing unnecessary work
  • PERFORMANCE: Replaced Array reflection calls with faster ArrayUtilities methods
    • Uses optimized array operations from java-util for better performance
  • FIX: ArrayFactory - Fixed converting subclass types unnecessarily
    • Added isAssignableFrom check before calling Converter
    • Only convert if value is NOT already assignable to the component type
    • Preserves subclass types in polymorphic arrays (e.g., java.sql.Date in Date[] arrays)
  • ADDED: Aliases for new JDK factory types
    • AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry
    • ReentrantLock, ReentrantReadWriteLock
    • Semaphore, CountDownLatch

4.71.0 - 2025-12-31

  • FIX: Added factories and writers for JDK classes with inaccessible private final fields on Java 9+:
    • Java 9+ module system blocks reflection access to private final fields in java.base module
    • JDK classes are not compiled with -parameters flag, so constructor parameter names are synthetic (arg0, arg1), preventing named parameter matching
    • New Factories (extract state from JsonObject, invoke constructors directly):
      • SimpleEntryFactory - Handles AbstractMap.SimpleEntry and AbstractMap.SimpleImmutableEntry
      • ReentrantLockFactory - Creates ReentrantLock with correct fairness setting
      • ReentrantReadWriteLockFactory - Creates ReentrantReadWriteLock with correct fairness setting
      • SemaphoreFactory - Creates Semaphore with permits and fairness
      • CountDownLatchFactory - Creates CountDownLatch with initial count
      • OptionalFactory - Creates Optional.empty() or Optional.of(value)
    • New Writers (serialize meaningful state instead of internal Sync fields):
      • ReentrantLockWriter - Writes fairness setting
      • ReentrantReadWriteLockWriter - Writes fairness setting
      • SemaphoreWriter - Writes available permits and fairness
      • CountDownLatchWriter - Writes current count
      • OptionalWriter - Writes value or empty marker
    • Impact: These JDK types now round-trip correctly through JSON serialization on all Java versions
    • Backward Compatible: No API changes, automatic factory/writer registration via config files
  • FIX: ObjectResolver - Fixed potential NullPointerException in traverseArray() when array elements are null
    • Added null check before calling getClass() on array elements created by createInstance()
  • SECURITY: ObjectResolver - Fixed bypass of security limits for unresolved references and missing fields
    • Changed 4 locations from direct collection access (unresolvedRefs.add(), missingFields.add()) to security-aware methods (addUnresolvedReference(), addMissingField())
    • These methods enforce maxUnresolvedReferences and maxMissingFields limits configured in ReadOptions
    • Prevents potential DoS attacks via unbounded memory consumption
  • FIX: EnumTests - Fixed flaky test failures caused by cached constructor accessibility
    • Added @BeforeEach with ClassUtilities.clearCaches() to ensure test isolation
  • DEPENDENCY: Updated java-util to version 4.71.0
    • Required for ArrayUtilities.getLength() optimization
    • FastReader line/column tracking removed for performance (use getLastSnippet() for error context)

4.70.0 - 2025-11-18

  • DEPENDENCY: Updated java-util to version 4.70.0 for FastReader performance improvements and coordinated release.
  • PERFORMANCE: JsonReader/JsonIo - Eliminated unnecessary String encoding/decoding in String-based parsing:
    • Added JsonReader(Reader, ReadOptions) constructor: Allows direct character-based input without byte stream conversion
    • Optimized JavaStringBuilder.parseJson(): Now uses StringReader instead of FastByteArrayInputStream, eliminating String → bytes → chars roundtrip
    • Before: String → bytes (encode UTF-8) → FastByteArrayInputStream → InputStreamReader (decode UTF-8) → FastReader
    • After: String → StringReader → FastReader (direct character access, no encoding/decoding)
    • Impact: Removes ~1-2% overhead and eliminates unnecessary byte[] allocation for all JsonIo.toJava(String) and JsonIo.toMaps(String) calls
    • Backward Compatible: Zero API changes, all internal optimizations
  • PERFORMANCE: JsonReader - Eliminated dummy stream creation in non-parsing constructors:
    • Optimized JsonReader(ReadOptions) constructor: No longer creates empty FastByteArrayInputStream/InputStreamReader/FastReader/JsonParser when only resolving JsonObject graphs
    • Optimized JsonReader(Resolver) constructor: Removed dummy ByteArrayInputStream allocation when constructor is used for object resolution (not stream parsing)
    • Before: Created full stream infrastructure (FastByteArrayInputStream → InputStreamReader → FastReader → JsonParser) that was never used
    • After: Sets input and parser to null when not needed for the use case
    • Impact: Reduced allocation overhead in JavaObjectBuilder path (used when converting existing JsonObject graphs to Java objects)
  • IMPROVED: JavaStreamBuilder - Removed redundant InputStream.close():
    • Before: Closed both JsonReader and underlying InputStream separately (double close)
    • After: Only closes JsonReader, which automatically closes the underlying stream
    • Impact: Cleaner code, no functional change (double close was safe but unnecessary)
  • PERFORMANCE: Optimized JsonParser.readValue() for faster JSON parsing (16% improvement in combined parsing):
    • Optimization #1: Consolidated digit cases ('0'-'9') in switch statement into single range check in default case
    • Optimization #2: Removed redundant pushback('{') and skipWhitespaceRead() cycle when parsing JSON objects
    • Optimization #3: Added fast-path if statements for '{' and '[' (most common JSON value types) before switch statement
    • Optimization #4: Added fast-path for regular fields (non-@ prefixed) in readJsonObject() to bypass switch statement overhead
    • Result: Combined parsing CPU reduced from 13.87% to 11.65% (-2.22% total, 16.0% faster)
    • Impact: readValue() 7.91% → 6.55% (-1.36%, 17.2% faster), readJsonObject() 5.96% → 5.10% (-0.86%, 14.4% faster)
    • Backward Compatibility: Zero API changes, all 2,112 tests passing
  • IMPROVED: ReadOptionsBuilder.returnAsJsonObjects() now automatically sets failOnUnknownType(false):
    • Semantic Consistency: Map-of-Maps mode is designed to work without requiring classes on classpath
    • Usability: Enables parsing any JSON structure (including unknown @type entries) without exceptions
    • Use Cases: HTTP middleware, log analysis, cross-JVM data transport - all work without domain classes
    • Override: If strict type validation is needed in Map mode, explicitly call .failOnUnknownType(true) after .returnAsJsonObjects()
    • Backward Compatibility: Minimal impact - only affects code using returnAsJsonObjects() with unknown types (previously failing)
    • MapResolver Alignment: Aligns API behavior with MapResolver's documented purpose of handling JSON without class dependencies
  • NEW API: Added explicit toMaps() methods for class-independent JSON parsing:
    • Methods: toMaps(String), toMaps(String, ReadOptions), toMaps(InputStream), toMaps(InputStream, ReadOptions)
    • Returns: Builder supporting .asClass() and .asType() for flexible type extraction (same as toJava())
    • Maximum Flexibility: Handles all valid JSON types - objects (Map), arrays (List), primitives (String, Integer, etc.), and null
    • Consistent API: Same fluent builder pattern as toJava() - identical extraction methods
    • Purpose: Makes Map mode obvious from API - no hidden ReadOptions flags required
    • Auto-Configuration: Automatically sets returnAsJsonObjects() mode (which sets failOnUnknownType(false))
    • Use Cases: HTTP middleware, log analysis, cross-JVM transport - all without domain classes on classpath
    • Examples:
      • Map<String, Object> map = JsonIo.toMaps("{...}").asClass(Map.class) - Parse JSON object to Map
      • List<Object> list = JsonIo.toMaps("[1,2,3]").asClass(List.class) - Parse JSON array to List
      • String str = JsonIo.toMaps("\"hello\"").asClass(String.class) - Parse JSON string to String
      • Object result = JsonIo.toMaps(json).asClass(null) - Parse any valid JSON type
      • JsonObject obj = JsonIo.toMaps(json).asClass(JsonObject.class) - Access preserved @type metadata
    • Zero Duplication: Delegates to existing toJava() infrastructure with pre-configured options
    • API Clarity: JsonIo.toMaps(json).asClass(Map.class) vs JsonIo.toJava(json, opts).asClass(Person.class) - intent is clear from method name
  • ENHANCED: Updated JsonIo class-level JavaDoc with prominent "Two Modes for Reading JSON" section:
    • Java Object Mode: toJava() - Requires classes, returns typed objects, compile-time safety
    • Map Mode: toMaps() - No classes required, returns Map graph, works with any JSON
    • Documentation: Clear examples, use case guidance, and feature comparison
    • Improved Discoverability: Two modes now obvious from API documentation
  • DEPRECATED: Marked toObjects() methods for removal in version 5.0.0:
    • toObjects(String, ReadOptions, Class<T>) → Use toJava(String, ReadOptions).asClass(Class)
    • toObjects(InputStream, ReadOptions, Class<T>) → Use toJava(InputStream, ReadOptions).asClass(Class)
    • toObjects(JsonObject, ReadOptions, Class<T>) → Use toJava(JsonObject, ReadOptions).asClass(Class)
    • Reason: Fluent builder API (toJava().asClass()) is more flexible and supports generic types via .asType()
    • Migration: Simple one-to-one replacement with improved API
  • API CLEANUP: Removed ThreadLocal buffer size configuration from ReadOptions and ReadOptionsBuilder:
    • Removed methods: getThreadLocalBufferSize(), getLargeThreadLocalBufferSize(), threadLocalBufferSize(), largeThreadLocalBufferSize(), addPermanentThreadLocalBufferSize(), addPermanentLargeThreadLocalBufferSize()
    • Reason: JsonParser.readString() reverted to simple state machine using instance field StringBuilder instead of ThreadLocal char[] buffers
    • Impact: API simplification - removed 6 public methods and 2 constants that are no longer needed
    • Tests removed: Deleted ThreadLocalBufferIntegrationTest.java (12 tests) and updated JsonParserSecurityLimitsTest.java to remove ThreadLocal buffer assertions
    • Migration: No action required - these configuration options had no effect after readString() revert
  • IMPROVED: WriteOptionsBuilder and ReadOptionsBuilder alias type name removal methods now use RegexUtilities for enhanced performance and security:
    • Methods updated: removeAliasTypeNamesMatching(), removePermanentAliasTypeNamesMatching() in both builders
    • Pattern Caching: Wildcard patterns (converted to regex) are now cached, eliminating redundant Pattern.compile() calls when same pattern is used multiple times
    • ReDoS Protection: Timeout-based regex execution (default 5000ms) prevents catastrophic backtracking from malicious wildcard patterns
    • Invalid Pattern Handling: Malformed patterns are handled gracefully with null checks instead of throwing exceptions
    • Thread Safety: All pattern operations are thread-safe with ConcurrentHashMap-based caching
    • Performance: Zero overhead - shared ExecutorService eliminates per-operation thread creation that previously caused 74% performance degradation
    • Backward Compatibility: All 2,124 existing tests pass with identical behavior for valid patterns
    • Security: Prevents regex-based DoS attacks when user-provided wildcard patterns are used for alias filtering
    • Note: These methods are called during configuration setup, not in JSON serialization/deserialization hot paths, so performance impact is minimal but security benefits are significant
  • CLEANUP: Removed unused import com.sun.tools.javac.util.StringUtils from Injector.java that was preventing compilation on JDK 17+

4.63.0 - 2025-11-07

  • DEPENDENCY: Updated java-util to version 4.3.0 for new DataGeneratorInputStream utility and other enhancements.
  • PERFORMANCE: Optimized Injector.inject() hot path for ~12% read performance improvement:
    • Removed redundant declaringClass.isInstance(object) check - Native call eliminated from every field injection
    • Cached system class check as boolean field (computed once at construction vs per-injection)
    • Cached field metadata (fieldType, fieldName, displayName) to avoid repeated method calls
    • Impact: Read performance improved from 365ms to 322ms on benchmark suite (~12% faster)
  • PERFORMANCE: Optimized EnumFieldFilter to avoid unnecessary field.getName() call:
    • Moved field name computation after isEnum() check for early exit on non-enum fields (99% of fields)
    • Minor cold path optimization during class metadata setup
  • FIX: Fixed bug in ArrayTest.testEmptySubArray() test:
    • Changed JsonIo.toObjects() to JsonIo.toJava() for correct API usage
    • Fixed variable references from outer[] array to outerList.get() for proper type handling
  • FIX: Fixed 2 disabled security tests in JsonReaderSecurityLimitsTest:
    • testObjectReferencesLimit_ShouldUseConfiguredLimit - Added .asClass() to trigger parsing and reference tracking
    • testReferenceChainDepthLimit_ShouldUseConfiguredLimit - Created proper 4-level reference chain and added .asClass() to trigger resolution
    • Both tests now properly validate DoS attack prevention mechanisms
  • CLEANUP: Removed 3 stale TODO comments:
    • Removed outdated TODO in MultiKeyMapTest about nested Set support (already implemented and tested)
    • Removed 2 misleading TODOs in TestGraphComparator and TestGraphComparatorList about IP address performance (no IP lookup exists, static init is fast)

4.62.0 - 2025-11-02

  • PERFORMANCE: Increased ArrayList initial capacity in JsonParser.readArray() from 16 to 64 - Reduces resize operations for typical JSON arrays. Each resize allocates new array (2x size), copies all elements, and triggers GC pressure. Initial capacity of 64 eliminates 1-2 resize operations for arrays with 16-64 elements while adding only ~200 bytes overhead per array. Expected improvement: 5-8% faster deserialization for JSON with arrays >16 elements (common in API responses, list endpoints, data exports).
  • PERFORMANCE: Eliminated redundant getClass() calls in serialization hot paths - Cached Class<?> at call sites and passed as parameters to methods, avoiding repeated getClass() invocations. Fixed two critical paths:
    • writeImpl()writeArray(): Cache objClass and pass to writeArray() instead of calling array.getClass() again
    • traceReferences()processArray(): Pass cached clazz to processArray(), eliminating array.getClass().getComponentType() redundancy
    • Expected improvement: 8-12% faster serialization performance in array-heavy workloads (getClass() is expensive when called millions of times)
  • PERFORMANCE: Optimized MapResolver.traverseCollection() to cache ReferenceTracker outside loop, eliminating redundant getReferences() calls during collection traversal.
  • PERFORMANCE: Removed redundant injector map caches that duplicated ReadOptions.getDeepInjectorMap() caching:
    • Removed MapResolver.classInjectorCache instance field - Was caching results already cached by ReadOptions via ClassValueMap
    • Removed local injectorCache in Resolver.patchUnresolvedReferences() - Method-local cache was redundant
    • Combined with ObjectResolver type cache removal: Overall ~4% improvement in deserialization performance
  • PERFORMANCE: Removed redundant type resolution cache from ObjectResolver - Type resolution is already cached by TypeUtilities.resolveType() in java-util, making the additional cache layer unnecessary. Simplifies code and eliminates duplicate string concatenation overhead.
  • FEATURE: Added convenience read methods to Resolver for cleaner ClassFactory implementations:
    • Primitive type methods: readString(), readInt(), readLong(), readFloat(), readDouble(), readBoolean() - Automatic type conversion via Converter
    • Complex type methods: readObject(), readArray(), readList(), readMap() - Full deserialization with cycles and references support
    • Benefits: No manual Map casting, no instanceof checks, eliminates boilerplate, symmetric API with WriterContext
    • Updated factories: MultiKeyMapFactory, CompactSetFactory, CompactMapFactory, EnumSetFactory now use convenience methods
    • Updated examples: CustomJsonTest, CustomJsonSubObjectTest, CustomJsonSubObjectsTest demonstrate usage
    • Comprehensive documentation: Added "Writing Custom ClassFactory (Reader)" section to user-guide.md with complete examples
  • ENHANCEMENT: Simplified MultiKeyMapFactory key resolution - Removed unnecessary Collections.unmodifiable*() wrapping of collection keys to avoid Sealable* serialization issues when MultiKeyMap is re-serialized. Keys are resolved directly via reader.toJava() for cleaner, simpler code. Also improved config parsing to use Converter methods instead of manual parsing.
  • REMOVED: MultiKeyMapFactory old format handling - Removed support for legacy array-with-marker-strings format that used internalizeMarkers() and old reconstructKey(). All MultiKeyMap serialization now uses the native List/Set format. Requires java-util 4.2.0+ which consolidated to single reconstructKey() method.
  • FEATURE: Added comprehensive semantic write API to WriterContext for easier custom writer implementation:
    • Basic field/value methods:
      • writeFieldName(String name) - Writes field name with colon (e.g., "name":)
      • writeValue(String value) - Writes string value with proper quote escaping
      • writeValue(Object value) - Writes any object value with full serialization
    • Complete field methods (with leading comma):
      • writeStringField(String name, String value) - Complete string field (e.g., ,"name":"value")
      • writeObjectField(String name, Object value) - Complete object field with automatic serialization
      • writeNumberField(String name, Number value) - Complete number field WITHOUT quotes (e.g., ,"count":42)
      • writeBooleanField(String name, boolean value) - Complete boolean field WITHOUT quotes (e.g., ,"active":true)
    • Composite field methods (combines comma + field name + opening bracket/brace):
      • writeArrayFieldStart(String name) - Comma + field name + [ in one call (e.g., ,"items":[)
      • writeObjectFieldStart(String name) - Comma + field name + { in one call (e.g., ,"config":{)
    • Structural methods:
      • writeStartObject() / writeEndObject() - Object braces
      • writeStartArray() / writeEndArray() - Array brackets
    • Benefits: Eliminates manual quote escaping, reduces boilerplate, prevents malformed JSON, provides Jackson-like semantic API
    • Updated MultiKeyMapWriter: Refactored to use new semantic API (uses writeArrayFieldStart() for cleaner code)
    • Comprehensive Javadoc: All 13 methods include detailed documentation with usage examples and patterns
  • FEATURE: Added complete serialization support for MultiKeyMap (from java-util):
    • Added MultiKeyMapWriter: Custom writer that serializes MultiKeyMap with shortened configuration format and externalized markers
    • Added MultiKeyMapFactory: Custom factory that deserializes MultiKeyMap, reconstructing original Set/List/Array key structures
    • Marker externalization: Internal markers (OPEN, CLOSE, SET_OPEN, SET_CLOSE) are converted to consistent tilde notation (~OPEN~, ~CLOSE~, ~SET_OPEN~, ~SET_CLOSE~) for clean JSON output
    • Collision handling: User strings matching marker names are automatically escaped with ~~ESC~~ prefix to prevent data corruption
    • Null key support: NULL_SENTINEL objects properly converted to/from null during serialization
    • Configuration preservation: All MultiKeyMap settings (capacity, loadFactor, collectionKeyMode, flattenDimensions, simpleKeysMode, valueBasedEquality, caseSensitive) preserved during round-trip
    • Comprehensive test coverage: 25 tests covering null keys, Set/List keys, nested arrays, complex objects, enums, temporal types, BigDecimal/BigInteger, UUID, primitive arrays, empty collections, marker collision, and string edge cases
  • ENHANCEMENT: Enhanced WriterContext.writeValue() with context-aware automatic comma management:
    • Context tracking: JsonWriter now maintains a context stack (ARRAY_EMPTY, ARRAY_ELEMENT, OBJECT_EMPTY, OBJECT_FIELD) to track the current JSON structure state
    • Automatic comma insertion: writeValue(Object) and writeValue(String) automatically insert commas based on context (e.g., between array elements, after object fields)
    • Benefits for custom writers: Eliminates manual "boolean first" comma tracking pattern - writers can simply call writeValue() in loops without any comma logic
    • Design principle: Unified API where one method (writeValue()) handles all value types (String, Number, Boolean, Object, null) with automatic type detection and comma management
  • ENHANCEMENT: Modernized custom writers to use new semantic WriterContext API:
    • Updated ByteBufferWriter: Refactored to use writeFieldName() and writeValue() instead of manual output writing
    • Updated CharBufferWriter: Refactored to use semantic API, eliminating 60+ lines of manual JSON escaping code
    • Updated CompactMapWriter: Replaced reflection-based configuration access with public getConfig() API from java-util
    • Updated CompactSetWriter: Refactored to use context-aware writeValue(), eliminating manual comma handling (removed "boolean first" pattern)
    • Updated LongWriter: Added documentation explaining why primitive writers use direct output writing (comma handling already managed by framework)
    • Benefits: Cleaner code, automatic JSON escaping, automatic comma management, better maintainability, consistent with new WriterContext patterns
  • TEST: Refactored buffer writer tests to use end-to-end JsonIo integration testing:
    • ByteBufferWriterTest: Converted from null-context unit tests to 4 comprehensive JsonIo integration tests
    • CharBufferWriterTest: Converted from null-context unit tests to 5 comprehensive JsonIo integration tests
    • Removed dead code: Eliminated null-check fallback paths that could never execute in production (WriterContext is never null)
    • Enhanced coverage: Added tests for empty buffers, full buffers, and comprehensive JSON escaping validation
    • Better testing: Tests now verify writers work correctly through real JsonIo pipeline with actual WriterContext
  • FIX: Fixed critical bug in java-util's ConcurrentList.toArray() methods where null elements were incorrectly treated as "vanished due to concurrent removal", causing early loop termination and data loss. Added comprehensive test coverage (testNullElementsInToArray()) with 4 test scenarios covering nulls in various positions, array-backed and typed arrays, and edge cases.

4.61.0

  • FEATURE: Added useUnsafe option to ReadOptions to control unsafe object instantiation. When enabled, json-io can deserialize package-private classes, inner classes, and classes without accessible constructors. This feature is opt-in for security reasons and uses thread-local settings to prevent interference between concurrent deserializations.
  • TEST: Fixed DistanceBetweenClassesTest.testPrimitives to accommodate changes in java-util 4.1.0's ClassUtilities.computeInheritanceDistance() method. The method now correctly recognizes primitive widening conversions (e.g., byte to int returns distance 2, short to int returns distance 1).
  • FIX: PR #426 - Windows compatibility fixes for json-io:
    • Fixed JsonObject.CacheState to implement Serializable for proper Java serialization support
    • Fixed EnumFieldFilter to correctly handle both $VALUES (standard JVM) and ENUM$VALUES (Windows JVM) synthetic fields
    • Fixed tests to handle Windows file path separators (backslash vs forward slash)
    • Changed SecurityTest to use cross-platform java --version command instead of ipconfig
    • Fixed JsonObjectTest serialization test typos
    • Fixed EnumFieldFilterTests to properly validate enum field filtering behavior
  • PERFORMANCE: Parser and Resolver optimizations
    • Skip injector resolution when there's no type context (null or Object.class), avoiding expensive reflection calls
    • Use Map.getOrDefault() for field substitutes to avoid double lookup
    • Pre-size ArrayList for arrays (16 elements) to reduce resizing operations
    • Hoisted ReadOptions.getMaxIdValue() to avoid repeated method calls
    • Removed duplicate array check in loadItems() method - signature already ensures Object[]
    • Optimized ObjectResolver.traverseArray() to use items array length directly instead of jsonObj.size()
    • Pre-size ArrayList collections in traverseCollection() to avoid resizing
    • Optimized traverseCollection() branch order - check common primitives first for fast path
    • Hoisted ReadOptions constants (maxUnresolvedRefs, maxMapsToRehash, maxMissingFields) to avoid repeated method calls
    • Replaced string concatenation in ObjectResolver type resolution cache with dedicated TypeResolutionKey class
    • Pre-size ArrayList collections in MapResolver.traverseCollection() to avoid resizing

4.60.0

  • FIX: Issue #424 - Fixed maxObjectGraphDepth incorrectly counting objects instead of actual depth. The depth limit was being triggered by the number of objects at the same level (e.g., a list with 12 elements at depth 2) rather than the actual nesting depth. The fix properly tracks depth for each object during traversal.
  • DOCUMENTATION: Issue #423 - Updated documentation to correctly reflect that the default unknown type is JsonObject (not LinkedHashMap). When unknownTypeClass is null and an unknown type is encountered, json-io creates a JsonObject which implements Map. Users can explicitly set unknownTypeClass to LinkedHashMap.class or any other Map implementation if desired.
  • VERIFIED: Issue #425 - Added comprehensive tests for nested JSON with unknownTypeClass(LinkedHashMap.class). The reported issue of inner object values being duplicated to the outer level could not be reproduced in the current version. Tests confirm correct behavior with various nesting levels and configurations.

4.59.0

4.58.0 (no release)

4.57.0

  • Updated java-util from 3.6.0 to 3.7.0.
  • ENHANCEMENT: Replace System.out.println/System.err.println with proper Java logging - all console output now uses java.util.logging with appropriate levels (LOG.info() for user-visible results, LOG.fine() for debug information, LOG.warning() for errors) for better build output control and maintainability
  • ENHANCEMENT: Add comprehensive test suite for automatic Insets support - java.awt.Insets objects now have complete test coverage with 12 test scenarios including serialization/deserialization, arrays, complex objects, converter integration, and edge cases, complementing the existing Enhanced Converter Integration
  • ENHANCEMENT: Add comprehensive test suites for automatic Point, File, and Path support - java.awt.Point objects now have complete test coverage with 12 scenarios, java.io.File and java.nio.file.Path have test coverage for Enhanced Converter Integration capabilities, validating string ↔ object conversions and type support detection
  • TESTS: Add AutomaticDimensionTest for comprehensive java.awt.Dimension testing - validates Enhanced Converter Integration with 6 test scenarios covering string serialization, array handling, and type detection
  • TESTS: Add AutomaticRectangleTest for comprehensive java.awt.Rectangle testing - validates Enhanced Converter Integration with 6 test scenarios covering string serialization, array handling, and type detection
  • ENHANCEMENT: Add aliases for AWT classes to improve JSON readability - java.awt.Point = Point, java.awt.Color = Color, java.awt.Rectangle = Rectangle, java.awt.Dimension = Dimension, java.awt.Insets = Insets aliases added to aliases.txt for cleaner JSON output without package prefixes

4.56.0

  • ARCHITECTURAL: Enhanced Converter support to/from: Point, Rectangle, Insets, Dimensions, Color, File, and Path to many other types.s
  • ARCHITECTURAL: Enhanced Converter integration in Resolver.createInstance() - prioritizes java-util Converter over custom factories for specific DTO types like java.awt.Color, java.awt.Rectangle, and java.awt.Dimension, enabling automatic string ↔ Object conversions for designated types only
  • ARCHITECTURAL: Add sophisticated source type detection for enhanced DTO conversion - analyzes JsonObject structure to identify common patterns (Color: r/g/b/a, Point: x/y, Rectangle/Dimension: width/height) for intelligent Converter-based transformations
  • ARCHITECTURAL: Preserve Throwable factory handling while enabling enhanced Converter integration - ensures exception type preservation continues to work through ThrowableFactory while allowing other types to benefit from automatic Converter support
  • ENHANCEMENT: Modernize public API generics with enhanced type safety - adds sophisticated generic bounds to JsonClassReader, JsonClassWriter, and collection handling methods following PECS principle for maximum flexibility while maintaining 100% backward compatibility
  • ENHANCEMENT: Improve collection method signatures with bounded wildcards - enhances WriteOptionsBuilder and ReadOptionsBuilder methods with sophisticated generic bounds like Collection<? extends String> and Map<? extends Class<?>, ? extends Collection<? extends String>> for better type safety and API flexibility
  • ENHANCEMENT: Add automatic Rectangle support - java.awt.Rectangle objects now serialize/deserialize automatically without custom factories, supporting both string format "(x,y,width,height)" and Map format with x/y/width/height properties
  • ENHANCEMENT: Add automatic Dimension support - java.awt.Dimension objects now serialize/deserialize automatically without custom factories, supporting both string format "widthxheight" and Map format with width/height properties
  • FIX: Resolve exception cause type preservation during JSON deserialization - fixes ThrowableFactory to properly maintain specific exception types (e.g., ExceptionWithStringConstructor) instead of falling back to generic Throwable, ensuring accurate exception reconstruction
  • SECURITY: Fix critical type safety vulnerability in ArgumentHelper.getNumberWithDefault() - prevents ClassCastException attacks
  • SECURITY: Fix unsafe type casting in JsonObject.rehashMaps() - adds instanceof validation before casting
  • SECURITY: Fix unsafe type casting in JsonParser.loadItems() - validates array types before casting
  • SECURITY: Fix infinite loop vulnerability in JsonReader.DefaultReferenceTracker - adds circular reference detection
  • SECURITY: Fix critical memory leak in JsonValue type cache - implements bounded cache with eviction to prevent OutOfMemoryError
  • SECURITY: Fix null pointer vulnerabilities in JsonValue - adds comprehensive null safety checks
  • SECURITY: Fix race condition in JsonValue type cache - uses atomic operations for thread safety
  • SECURITY: Fix memory leak in JsonWriter.traceReferences() - implements object count and depth limits to prevent unbounded memory growth
  • SECURITY: Fix unsafe reflection access in JsonWriter.getValueByReflect() - adds comprehensive null and security checks with graceful failure
  • SECURITY: Fix input validation in JsonWriter.writeJsonUtf8String() - adds null safety and string length limits to prevent memory issues
  • SECURITY: Add bounds checking in JsonWriter primitive array writers - prevents buffer overflow attacks in byte and int array processing
  • PERFORMANCE: Updated JsonParser.readString() to March 3, 2025 version with full Unicode surrogate pair support - removed ThreadLocal buffers from later versions, added lookup tables (ESCAPE_CHAR_MAP, HEX_VALUE_MAP) and fast path optimization for regular characters. Correctly handles Unicode characters beyond Basic Multilingual Plane (emoji, mathematical symbols, ancient scripts) using surrogate pairs. Performance: 0.9% faster than Feb 11 simple state machine while providing superior Unicode correctness.
  • SECURITY: Fix null validation in JsonParser.loadId() and loadRef() - adds comprehensive null checks and ID range validation to prevent attacks
  • SECURITY: Add bounds checking for escape processing in JsonParser.processEscape() - prevents buffer overflow in Unicode escape sequence handling
  • Fix resource leak in JsonIo.JavaStreamBuilder.asType() - ensures proper cleanup of JsonReader and InputStream resources with enhanced error handling
  • Simplify exception handling in JsonIo.toJson() methods - improves maintainability while preserving original JsonIoException types
  • Simplify cache management system in JsonObject - consolidates multiple cache variables into unified CacheState class for improved maintainability
  • SECURITY: Fix unbounded object reference tracking in JsonReader.DefaultReferenceTracker - adds configurable limits (10M objects, 10K chain depth) to prevent DoS attacks
  • SECURITY: Improve circular reference detection in JsonReader.DefaultReferenceTracker - enhanced tracking with depth limits and better error reporting
  • SECURITY: Add input validation for enum type coercion in JsonReader - validates enum string length and content to prevent malicious input attacks
  • SECURITY: Fix unbounded memory consumption in Resolver collections - adds configurable limits (1M objects, 10K stack depth) to prevent DoS attacks
  • SECURITY: Add bounds checking in Resolver.setArrayElement() method - validates array indices to prevent buffer overflow attacks
  • SECURITY: Implement depth limits for traversal operations in Resolver - prevents stack overflow via malicious deeply nested JSON structures
  • SECURITY: Fix unbounded collection processing in MapResolver - adds configurable limits (1M objects) to prevent memory exhaustion attacks
  • SECURITY: Improve type safety in MapResolver casting operations - adds validation before type conversions to prevent unsafe casting vulnerabilities
  • SECURITY: Add string length validation in MapResolver - prevents memory exhaustion via extremely large string fields (64KB limit)
  • SECURITY: Add reference ID validation in MapResolver - prevents malicious negative reference ID attacks
  • SECURITY: Fix directory traversal vulnerability in MetaUtils.loadMapDefinition() and loadSetDefinition() - adds resource path validation to prevent unauthorized file access
  • SECURITY: Add input validation in MetaUtils resource loading methods - prevents memory exhaustion via file size (1MB), line count (10K), and line length (8KB) limits
  • SECURITY: Improve type safety in MetaUtils.getValueWithDefault() methods - adds graceful handling of ClassCastException with detailed error messages
  • SECURITY: Add bounds checking in MetaUtils.getJsonStringToMaxLength() - prevents memory issues with 64KB string length limit and exception handling
  • SECURITY: Secure reflection operations in Injector class - adds comprehensive security manager validation for field access, setAccessible() calls, and final modifier removal
  • SECURITY: Enhance VarHandle security in Injector - adds permission checks and graceful fallback to Field.set() when VarHandle creation fails due to security restrictions
  • SECURITY: Add object type validation in Injector.inject() - prevents injection into incorrect object types and adds extra protection for system classes
  • SECURITY: Improve MethodHandle security in Injector - adds validation for method access permissions and proper error handling for access violations
  • SECURITY: Secure reflection operations in Accessor class - adds comprehensive security manager validation for field access, setAccessible() calls, and MethodHandle operations
  • SECURITY: Enhance field access security in Accessor - adds permission checks with graceful fallback to Field.get() when MethodHandle creation fails due to security restrictions
  • SECURITY: Add object type validation in Accessor.retrieve() - prevents field access on incorrect object types and adds extra protection for system classes
  • SECURITY: Improve error handling in Accessor - graceful fallback for JDK internal classes while maintaining security for user classes
  • Fix null pointer exception in JsonReader.isRootArray() - adds null guard
  • Optimize JsonReader.getErrorMessage() performance with StringBuilder to reduce GC pressure
  • Optimize JsonParser.skipWhitespaceRead() with lookup table for whitespace characters
  • Add fast path for single-digit integer parsing in JsonParser.readNumber()
  • Use switch statement instead of if-else chain in JsonParser.readValue() for better performance
  • Optimize JsonParser.readToken() with fast ASCII case conversion for common tokens
  • Optimize JsonParser string buffer management with size-based strategy
  • Optimize Resolver.patchUnresolvedReferences() with injector caching and reduced object lookups
  • Optimize ObjectResolver.traverseCollection() with early null exits and reduced getClass() calls
  • SECURITY: Add comprehensive security audit logging system via SecurityAuditLogger - provides detailed security event tracking, performance monitoring, and incident response capabilities for production deployment
  • SECURITY: Add comprehensive fuzz testing framework via SecurityFuzzTest - validates protection against malicious JSON inputs including deeply nested structures, large collections, massive strings, circular references, unicode injection, numeric overflow, and reference manipulation attacks
  • SECURITY: Add advanced attack simulation testing via SecurityAttackSimulationTest - models real-world attack scenarios including billion laughs attacks, zip bombs, hash collision attacks, regex DoS attacks, prototype pollution attacks, timing attacks, memory disclosure attacks, and concurrent attacks
  • SECURITY: Add comprehensive performance benchmarking via PerformanceBenchmarkTest - validates that security improvements maintain excellent performance with detailed metrics for serialization, deserialization, large collections, reflection caching, and memory efficiency
  • SECURITY: Add production security documentation via SECURITY.md - comprehensive security guide covering security features, configuration, attack protection, best practices, incident response, and compliance for secure deployment
  • Optimize MapResolver methods with early null exits and cached method lookups
  • Optimize JsonReader methods by caching getClass() calls and using final variables
  • Optimize Injector.inject() exception handling with cached field information

4.56.0

  • Updated java-util from 3.4.0 to 3.5.0.
  • Jar now built with the -parameters flag enabling constructor parameter name matching
  • Improved object instantiation logic to use parameter names when available
  • Expanded ThrowableFactory to support parameter name aliases
  • Throwable instantiation now delegates to Converter for faster construction
  • Fixed exception message selection when using ThrowableFactory
  • Preserve null cause when constructing exceptions via ThrowableFactory
  • Treat empty cause objects as null during exception instantiation
  • Ensure message field is written first to preserve constructor argument order
  • Minor fixes and test updates
  • Reflection usage in ReadOptionsBuilder and Injector now leverages ReflectionUtils caching
  • Optimized JsonObject.get() method with binary search for sorted String keys (>8 elements)
  • Cache keySet() and values() collections in JsonObject to avoid repeated creation
  • Optimize containsKey() and containsValue() with early exit strategies and type filtering
  • Optimize entrySet() with custom iterator to reduce object allocations for array-based data
  • Optimize hashCode() calculation by avoiding IdentityHashMap overhead for simple arrays
  • Add fast path for isEmpty() check to avoid size() calculation overhead
  • Optimize putAll() with bulk operations and single hash invalidation
  • Cache sorted state in JsonObject to eliminate O(n) scans on every get() operation for large arrays
  • Cache array lengths in JsonObject to eliminate expensive Array.getLength() JNI calls

4.55.0

  • Updated java-util from 3.3.2 to 3.4.0.
  • Added class-level Javadoc for ByteArrayWriter describing Base64 encoding
  • Fixed deserialization of Java records
  • Tests now create record classes via reflection for JDK 8 compile compatibility.
  • InjectorPrivateConstructorsTest now uses ReflectionUtils for reflective calls.
  • Replaced System.out.println debug output with Java logging via LoggingConfig
  • SealableNavigableMap now wraps returned entries to enforce immutability
  • Preserve comparator when constructing SealableNavigableSet from a SortedSet
  • Documentation expanded for CompactMap usage and builder() caveats
  • JsonObject exposes getTypeString() with the raw @type value
  • Fixed TestUtil.serializeDeserialize to retain Enum type information
  • Pinned core Maven plugin versions to prevent Maven 4 warnings
  • Fixed SealableNavigableSet.retainAll to correctly return modification status
  • Fixed SealableNavigableSet.addAll to report modifications
  • Corrected assertion for retainAll result in SealableNavigableSetAdditionalTest
  • Tests avoid ByteArrayOutputStream.toString(Charset) for JDK 8 compatibility
  • Documentation updated with guidance for parsing JSON that references unknown classes
  • Enum alias/coercion tests now force type info to be written so enums deserialize correctly
  • Updated EnumBasicCreationTest to expect enum values serialized as simple strings
  • RecordFactory now uses java-util ReflectionUtils
  • Adjusted TreeSet substitution test to check assignability of Set type
  • Root-level enums with fields now include type info for reliable deserialization
  • Added String-to-enum fallback conversion for root objects
  • Fixed SealableNavigableSet.tailSet(E) to include the starting element
  • Expanded SingletonList tests for branch coverage
  • Fixed primitive array conversion handling in Resolver.valueToTarget
  • Fixed EnumSet traversal when enum element lacks a name
  • Fixed VarHandle reflection to allow private-constructor injector
  • Added unit tests for JsonObject map-view methods
  • Fixed VarHandle injection using a MethodHandle
  • Fixed VarHandle injection invocation for reflection-based Injector
  • Fixed Injector method-based creation to correctly locate void setters
  • Injector.create now supports invoking package-private and private setter methods
  • RecordFactory now checks the Java version before using records
  • SealableSet(Collection) now copies the supplied collection instead of wrapping it
  • Added unit test for Unicode surrogate pair escapes
  • Added APIs to remove permanent method filters and accessor factories
  • Added unit test for removePermanentAccessorFactory
  • Fixed failing test for non-simple root type handling
  • Fixed enum round-trip test to specify target class
  • Added tests covering Pattern and Currency serialization
  • Added Javadoc for ZoneIdWriter describing its behavior
  • Updated JsonReaderHandleObjectRootTest to expect JsonIoException on return type mismatch
  • Added regression test for ISO Timestamp Map conversion

4.54.0 Updated to use java-util 3.3.1

4.53.0 Updated to use java-util 3.3.1

  • Updated java-util from 3.3.0 to 3.3.1.
  • Removed unused ClassFactories.
  • Added ReadOptions/WriteOptions ability to specify notCustmoRead/Written classes in notCustomRead.txt and notCustomWritten.txt

4.52.0 CompactMap and CompactSet Enhancements

  • CompactMap and CompactSet JSON formats improved - match historical formats, except when using builder pattern.
  • ByteBuffer and CharBuffer converstions to/from Map added.
  • Performance improvements in JSON serialization and deserialization.
  • Code simplification related to instance creation for common types.
  • Updated java-util from 3.2.0 to 3.3.0.

4.51.0 CompactMap and CompactSet Enhancements

  • JSON Serialization Improvements
    • Implemented specialized JSON serialization for CompactMap and CompactSet with optimized format
    [{"@type":"com.cedarsoftware.util.CompactMap","config":"java.util.HashMap/CS/S70/id/Unord","data":[]},
    {"@type":"com.cedarsoftware.util.CompactSet","config":"CI/S30/Ord","data":[]}]
    • Added tests to ensure reference identity maintained across JSON round-trips
    • Preserved configuration details during serialization/deserialization
  • Collection API Consistency
    • Aligned CompactSet API with CompactMap for better usability
  • Improved JsonObject Implementation
    • Consistent Map Interface: Enhanced JsonObject to implement the Map interface more consistently:
    • Fixed the internal storage mechanism to properly handle different storage states
    • Ensured Map methods work correctly regardless of internal storage approach
  • Documentation and Testing
    • Added comprehensive test suite for JSON serialization and deserialization
    • Added tests for complex reference scenarios and edge cases
    • Improved JavaDoc comments for new public methods
  • Updated java-util from 3.1.1 to 3.2.0.

4.50 Major Improvements to Type Resolution and API Usability

  • Enhanced Type System Support
    • json-io now leverages java-util's sophisticated TypeUtilities framework to provide comprehensive resolution of complex Java generics:
      • Full support for ParameterizedTypes (e.g., Map<String, List<Person>>)
      • Proper handling of GenericArrayTypes for arrays with generic components
      • Resolution of TypeVariables in generic class hierarchies
      • Support for WildcardTypes (? extends, ? super) in generic declarations
      • These improvements enable more accurate field type inferencing during Java object resolution, particularly for complex nested generic structures.
    • Performance improvements in parsing and resolving.
  • New Type-Aware API Capabilities
    • Added advanced type specification via the new TypeHolder class:
      • Enables capturing and preserving full generic type information at runtime
      • Allows specifying complex parameterized types as deserialization targets
      • Example: new TypeHolder<Map<String, List<Person>>>() {}
    • The JsonIo class now accepts TypeHolder instances, allowing precise type targeting beyond what's possible with raw Class references alone.
  • Fluent Builder API for Improved Developer Experience
    • Introduced a new fluent builder pattern through the JsonIo.toJava() API family:
      • More intuitive, chainable API that clearly separates the input source from the target type
      • Provides dedicated methods for different input types (String, InputStream, JsonObject)
      • Offers type-specific terminal operations:
        • asClass(Class<T>) for simple class types
        • asType(TypeHolder<T>) for complex generic types
      • Examples:
        // Simple class types
        Person person = JsonIo.toJava(jsonString, options).asClass(Person.class);
        
        // Complex generic types
        List<Person> people = JsonIo.toJava(jsonString, options)
                                  .asType(new TypeHolder<List<Person>>(){});
      • Eliminates ambiguity in method signatures while providing a more expressive API
      • Facilitates better IDE code completion and suggestion
  • Updated java-util from 3.0.3 to 3.1.0.

4.40.0

  • All Time and Date classes compressed to a single String representation in JSON, uses ISO formats when possible.
  • Java's Pattern and Currency support added
  • Updated java-util from 3.0.2 to 3.0.3.

4.33.0

  • New custom ClassFactory classes are easier to write:
  • ByteBuffer and CharBuffer now natively supported.
  • CompactMap supported added via CompactMapFactory and CompactMapWriter.
  • Sealable* tests moved from java-util to json-io (and so have the Sealable* classes).

4.32.0

  • EnumSet can now be written with @type or @enum, controlled by a WriteOption (writeEnumSetOldWay). Currently, the default is true, write the old way for backward compatibility. This will change in a future release.
  • JsonObject simplified, with @keys and @items now as explicit fields.
  • JsonObject simplified, enumType has been removed, as it is now stored in JavaType field.
  • Root object types like Person[].class or String[].class supported for casting, as opposed to only Object[].class.

4.31.0

  • scrub release.

4.30.0

  • Root object type's like Person[].class, String[].class, can now be specified as the rootType and the return value will be Person[], String[], or a ClassCastException if the JSON data does not match the type.
  • JsonIo.formatJson() three parameter version removed. Use the one (1) parameter API that takes the JSON to format. It runs much faster, as it no longer deserializes/serializes, but walks the JSON String directly.

4.29.0

  • Consumed java-util's ClassUtilities.getClassLoader(), which obtains the classLoader in a more robust way and works in OSGi and JPMS environment or non-framework environment
  • Removed slf4j and logback-classic from test dependencies
  • Merged in PR #297 by DaniellaHubble: Fix test that fails unexpectedly in testEnumWithPrivateMembersAsField_withPrivatesOn()
  • Updated java-util from 2.15.0 to 2.17.0.

4.28.0

4.27.0

  • ReadOptionsBuilder.addInjectorFactory() added to allow additional InjectorFactory's to be added.
  • ReadOptionsBuilder.addFieldFilter() added to allow additional FieldFilters to be added.
  • LRU size control added to ReadOptionsBuild and WriteOptionsBuilder. These control the LRU size of the cache that maps Classes to Fields, Classes to Injectors, and Classes to Accessors.
  • Adds bigint, BigInt, bigdec, BigDec, String, Date, and Class to aliases to java-util's ClassUtilities.forName() support
  • Updated java-util from 2.13.0 to 2.14.0.

4.26.0

  • Performance improvement for JsonIo: When using null for default ReadOptions or WriteOptions, the same static instance is used.
  • Updated java-util from 2.10.0 to 2.13.0.

4.25.0

  • JsonParser now uses an instance-based cache for common values, not a static one. This will allow for more speed during concurrent parsing.
  • Within aliases.txt, java.time.zone.ZoneRules = ZoneRules is now correctly specified (it had java.time.ZoneRules before).
  • When null passed in for ReadOptions or WriteOptions to JsonIo APIs, an already created default instance of ReadOptions or WriteOptions is returned to improve performance (no need to reconstruct the default instance).
  • Updated java-util from 2.9.0 to 2.10.0.

4.24.0

  • All aliases have been moved to aliases.txt in the resources folder. It is a very complete list of class names to alias names. If you want less aliases (or more) substituted on writing JSON, use the addPermanentAlias() APIs on ReadOptionsBuilder and WriteOptionsBuilder. If you do not want a particular alias output, use WriteOptionsBuilder.removeAliasedClassName(wildcardPattern). The API is available for all Read/WriteOptions, the "permanent" APIs on the builder, or for a specific Read/WriteOptions instance.
  • The "extendedAliases" option has been removed from Read/Write options builders. By default, as many aliases are enabled as possible, and you can use the removeAliasXXX APIs to reduce them, or place your own version of aliases.txt in the classpath ahead of the one in json-io.jar.
  • WriterContext.getObjsReferenced() added, which has all objects id to Object to allow custom writers to write @id, @ref if desired.

4.23.0

  • Collections.unmodifiableXXX() instances when serialized, restore back to unmodifiable instances.
  • ImmutableList and ImmutableSet restore back unmodifiable instances.
  • ReadOptionsBuilder now include all extended aliases by default (.withExtendedAliases()). You can take advantage of this on the sending side by using the WriteOptionsBuilder().withExtendAliases(). We will default this on the WriteOptionsBuilder in the future as the new default makes it "out there." Remember: You can read them even if they are not sent, but you can't write them if the reader is not ready for them.

4.22.0

  • Many more @type aliases added to keep the JSON succinct and more human-readable.
  • Broader conversion support for rootTypes: JsonIo.toObjects(..., rootType) Includes all the java-utils Converter.convert() pairings (680+)
  • Removed stack argument from CustomReader. When creating a CustomReader, use the passed in resolver.push(node) to push objects onto the stack for later processing (custom or not). See example in UserGuide (coming shortly).

4.21.0

  • Empty Lists, Sets, and Maps enforce 'emptiness' on reconstruction
  • Singleton Lists, Sets, and Maps enforce 'singleton-ness' on reconstruction
  • Synchronized Lists, Sets, and Maps enforce 'synchronized-ness' on reconstruction
  • Fixed NPE on null writeOptions for JsonIo.toJson(). The writeOptions are now created with defaults for you if null is passed in.
  • Added Resolver as the last argument to the JsonClassReader.read() method. The author is not required to use the Resolver in their implementation, but it does it come in handy as it has the Map of IDs to JsonObjects, as well as the ReadOptions, and the Converter.
  • Deprecated the APIs on JsonIo that exist to show one how to convert the old style Map options to the new "builder" format.

4.20.0

  • MethodFilter can be applied to remove the use of a method accessor, useful when the method accessor is causing problems (additional unwanted side-effects) during the serialization (outputting of JSON). MethodFilter's are added to WriteOptions via the WriteOptionsBuilder. MethodFilter's can be added to a single WriteOptions instance or added permanently (jvm lifecyle) so that all created WriteOptions include it automatically (see WriteOptionsBuilder.addPermanent* APIs).
  • Significant updates made to User Guide documentation.
  • pom.xml file updated to support both OSGi Bundle and JPMS (Modules).
  • module-info.class resides in the root of the .jar but it is not referenced.

4.19.13

  • ReadOptionsBuilder did not have the withExtendedAliases() option. This adds in all the config/extendedAliases.txt aliases, dramatically shrinking the size of common Java class names in the JSON @type field.
  • Both ReadOptionsBuilder and WriteOptionsBuilder can take an existing ReadOptions or WriteOptions as a starting point, allowing you to copy from an exist options, and then tweak it from there.

4.19.12

  • Added JsonIo.getReadOptionsBuilder(Map options) and JsonIo.getWriteOptionsBuilder(Map options) to facilitate porting over code that users older Map-based options.
  • Removed classes that were packaged in the adapter layer com.cedarsoftware.util.io.* All classes now start at com.cedarsoftware.io.* You will have to adjust your imports.
  • Bug fix: Arrays that did not have a type specified, but the array type could be inferred, the component types was incorrectly being set as the array type, not the component type.
  • Updated java-util from 2.4.6 to 2.4.8.

4.19.11

  • Removed references to JsonObject from transition classes com.cedarsoftware.util.io.JsonReader.

4.19.10

  • Updated transition class com.cedarsoftware.util.io.JsonReader. This API is for temporary transition to JsonIo class static APIs.
  • Added transition class com.cedarsoftware.util.io.JsonWriter. This API is for temporary transition to JsonIo class static APIs.
  • The entire packaging of JsonIo has been moved from com.cedarsoftware.util.io to com.cedarsoftware.io, except for the transition APIs.
  • Added the ability to put custom options (key/value pairs) on WriteOptions and ReadOptions.

4.19.9

  • NOTE: Repackaged resources into config/resources

4.19.8

  • NOTE: Repackaged com.cedarsoftware.util.io to com.cedarsoftware.io
  • NOTE: Repackaged com.cedarsoftware.util.reflect to com.cedarsoftware.io.reflect
  • You will need to adjust the import statements for any consuming classes.

4.19.7

  • Handle NoSuchMethodError() quietly, for shaded accessFactories that were added by containing platform, that no logner exist.

4.19.6

  • Added additional build properties to manifest.mf
  • Renamed method on AccessorFactory that changed signature, due to conflicts running inside container that also uses this library.

4.19.5

  • Updated JsonReader for backwards compatibility by adding jsonToJava(), jsonToMaps(), jsonObjectsToJava() static APIs.

4.19.4

  • In ReadOptionsBuilder and WriteOptionsBuilder, when loading dynamic items (class names, aliases, etc.) from resources, output warnings as opposed to throwing exceptions.

4.19.3

  • Remove ReflectionUtils from json-io as it is part of java-util.
  • Updated java-util from 2.4.4 to 2.4.5.

4.19.2

  • Moved more settings/properties from source code to resource files.

    Example changes required due to this update:
    Before

    A. Employee e = (Employee) JsonReader.jsonObjectsToJava(JsonObject employee, readOptions)
    

    After

    A. Employee e = JsonIo.toObjects(JsonObject, readOptions, Employee.class)
    

4.19.1

  • The old Map options method has been superceded by passing instead a WriteOptions or ReadOptions instance. All the prior features are still supported, plus new features have been added. Use the methods on WriteOptionsBuilder and ReadOptionsBuilder to set them.

    Example changes required due to this update:
    Before

    // Using [key: value] to indicate a Map 
    A. String json = JsonWriter.objectToJson(srcObj, [JsonWriter.TYPE: false])
    B. JsonWriter.objectToJson(srcObj)
    C. String json = JsonWriter.toJson(srcObj, null)
    D. String json = JsonWriter.formatJson(json)
    E. Map axisConverted = (Map) JsonReader.jsonToJava(json, [JsonReader.USE_MAPS:true])
    F. JsonWriter.writeJsonUtf8String(value, writer)
    

    After

    A. String json = JsonIo.toJson(srcObj, new WriteOptionsBuilder().showTypeInfoNever().build());
    B. JsonIo.toJson(srcObj)
    C. JsonIo.toJson(srcObj, null) // 2nd arg is WriteOptions instance (can be null for defaults)
    D. return JsonIo.formatJson(json)
    E. ReadOptionsBuilder builder = new ReadOptionsBuilder().returnAsMaps().build() 
       Map axisConverted = JsonIo.toObjects(json, builder.build(), null)  // 3rd param can be root class
    F. JsonWriter.writeJsonUtf8String(writer, value)
    

4.19.0

  • User Guide documentation on how to specify "options" to JsonReader/JsonWriter the new, easier way. The old Map options method has been superceded by the WriteOptions and ReadOptions approach. All the prior options are still supported, plus new features have been added.

4.18.0

  • Bug fix: When Enums were sent the "old way," (JSON object form) there was a bug in outputting additional fields defined on an Enum.
  • Enhancement: Improvements on Object construction for difficult to instantiate classes.

4.17.0

  • Java class instantiation has been improved and the related code has been refactored to a much better state.
  • More built-in types are moving to use the ClassFactory and JsonClassWriter, simplifying the code base further.
  • Continuing to refine the JsonReader/JsonWriter API, Deprecated older redundant methods.
  • There will be more releases of the 4.x branch, including support for specifying a root class to load from, removing the @type output for root fields (Issue #122, #150), support for field names without quotes and JSON comments, to name a few.
  • Upcoming version 5.0.0 will drop the dedicated methods.
  • Upcoming version 6.0.0 will move to JDK 11 syntax.

4.16.0

  • JsonReader/JsonWriter Options are now specified using ReadOptionsBuilder.build() and WriteOptionsBuilder.build().
  • For improved security, key JDK classes like ClassLoader, Process (and derived classes), Method, Field, Constructor and others are not serialized.
  • Fixed Issue #185 Serializing/deserializing SingletonMap/List/Set.
  • Performance improvement: Reads and Writes are much faster due to improved low-level stream handling. More to come on read performance improvements.
  • The public API on JsonReader and JsonWriter has been simplified to fewer options and many of the prior APIs have been deprecated. The 5.0.0+ release will remove these deprecated APIs.

4.15.0

  • Supports JDK1.8, JDK11, 17, 21. Tested with these versions, and compiled in class file version 52 (JDK1.8 ) format.
  • ClassFactory added isFinalObject() { return true/false } to prevent additional processing from happening if the ClassFactory creates the object AND assigns all values.
  • Fixed an issue with classes that used custom reader/writers being loaded when inside an array or collection. If there were circular references, they were not resolved correctly.
  • This version writes Enums in a more compact way with the field name associated to a JSON String name of the enum. However, the prior versions of json-io wrote Enums out as JSON objects. The JSON reader will read Enums either way. If you want the output to continue to write Enums as a JSON Object, use the .writeEnumsAsObjects() on the WriteOptionsBuilder, and it will output enums as it used to.
  • Minor change: JsonObject was JsonObject<K, V> and is now JsonObject (no generics). If you used JsonObject in your code, make sure to remove the generics.
  • Minor change: JsonReader.ClassFactory::newInstance(Class c, Object) has been changed to JsonReader.ClassFactory::newInstance(Class<?>, JsonObject). If you have written a CustomClassFactory, update the method signature to newInstance(Class<?>, JsonObject).

4.14.2

  • Enum/EnumSet support fully added @kpartlow
  • WARN This version inadvertently slipped to JDK11+ (which has been corrected in 4.15.0). Version 5.x.x will be JDK11 or JDK17.

4.14.1

  • JDK 1.8 is target class file format. @laurgarn
  • JDK 11 is source file format. @laurgarn
  • Bug fix: EnumSet support fixed. @laurgarn
  • Bug fix: Null boxed primitives are preserved round-trip. @laurgarn
  • Enhancement: Filter Blacklisted Fields Before Trying to Access them to prevent exceptions thrown by Proxies (improve hibernate support) @kpartlow
  • Bug fix: Stack overflow error caused by json-io parsing of untrusted JSON String @PoppingSnack
  • Enhancement: Create gradle-publish.yml @devlynnx
  • Enhancement: Added record deserialization, which implies java 16 codebase @reuschling
  • Bug fix: Fixed TestJavaScript @h143570
  • Enhancement: Bump gson from 2.6.2 to 2.8.9 @dependabot
  • Enhancement: support deserialization of Collections.EmptyList on JDK17 @ozhelezniak-talend

4.14.0

  • Bug fix: Enum serialization error with Java 17 #155. According to @wweng-talend, if you set : "--illegal-access=deny" on jvm parameters, it works the same between jdk11 and jdk17.
  • Bug fix: java.lang primitives serialization - JDK-8256358 - JDK 17 support #154. Fix by @wwang-talend.
  • Bug fix: failed to deserialize EnumSet with json without type #120. Fix by @sgandon and @wwang-talend

4.13.0

  • Enhancement: Clear unresolved references after all have been processed, as opposed to removing each one after it was processed.

4.12.0

  • Bug fix: Enhancement #137 introduced bug for negative numbers on simple values when tolerant/lenient parsing of +/- infinity was turned on.

4.11.1

  • Enhancement (#140): New option flag added FORCE_MAP_FORMAT_ARRAY_KEYS_ITEMS:true|false to allow forcing JSON output format to always write Map as @keys/@items in the JSON (example: {"@keys":["a", "b"], "@values":[1, 2]}, rather than its default behavior of recognizing all String keys and writing the Map as a JSON object, example: {"a":1, "b":2}. The default value for this flag is false.

4.11.0

  • Enhancement (#137): Allow tolerant/lenient parser of +/- infinity and NaN. New API added, JsonReader.setAllowNanAndInfinity(boolean) and JsonWriter.setAllowNanAndInfinity(boolean). The default is false to match the JSON standard.
  • Enhancement (#129): JsonReader.jsonToJava("") or JsonReader.jsonToJava(null) now returns a null, rather than throwing an exception.
  • Bug fix (#123): Removed vulnerability by disallowing ProcessBuilder to be serialized.
  • Bug fix (#124): Illegal Reflective Access warning when using json-io in Java 9 or newer. This was do to call isAccessible() on Java's Field class. This has been removed.
  • Bug fix (#132, #133): There was instance when @i was written when it should have been @e, indicating items, when using SHORT_META_KEYS flag.
  • Bug fix (#135): When reading { "@type": "char", "value": "\"" }, the value was read in as \u0000. It now reads in correctly as a double quote character.

4.10.1

  • Enhancement: Made FastPushbackBufferedReader constructor public so that this stream reader can be used anywhere.

4.10.0

  • Bug fix: When reading into Maps, logical primitives that are not long, double, boolean, or null, were being kept in JsonObjects instead of being converted into their respective types (int, float, Date, etc.)

4.9.12

  • Bug fix: Line number was incorrectly being reported as column number in error output.

4.9.11

  • Enhancement: Added nice JSON-style argument format method, typically used for logging method calls. See MetaUtils.getLogMessage().

4.9.10

  • Bug fix: When system property file.encoding was not set to UTF-8, json-io was not correctly handling characters outside the ASCII space. @rednoah

4.9.9

  • Enhancement: Missing field handler improvements. Submitted by @sgandon

4.9.8

  • Enhancement: Missing field handler improvements. Submitted by @sgandon

4.9.7

  • Enhancement: Added JsonReader.addReaderPermanent() and JsonWriter.addWriterPermanent() to allow for a static (lifecycle of JVM) reader / writer to be added. Now, custom readers and writers can be added that only exist per-instance of JsonReader / JsonWriter or permanently, so they do not have to be added each instantiation (through args or call .addReader() or .addWriter()).

4.9.6

  • Enhancement: Improved enum handling. Updated how enums are detected so that subclasses of enums are detected. ordinal and internal fields no longer output.

4.9.5

  • Bug fix: The new FastPushBackBytesReader was incorrectly reading a String byte-by-byte ignoring the code point boundaries. Because of this, it would blow up during parsing Strings with characters outside the ascii range. New test case added that causes the failure. For time being, the FastPushBackBytesReader has been removed.
  • Javadoc updates.

4.9.4

  • Optimization: The coercedTypes Map in the Resolver is built one time now.
  • Added test case illustrating gson cannot handle writing then reading back Maps correctly when the keys are not Strings.

4.9.3

  • Enhancement: Double.INF and NAN are output as null.

4.9.2

  • Optimization: When parsing from String, a different (faster) byte[] based pushback reader is used.
  • Optimization: Built-in Readers and Writers are only instantiated once for all instances of JsonReader / JsonWriter and then re-used.
  • Enhancement: Inner 'view' classes generated from .keySet() and .values() are coerced to standard mutable collection classes.
  • Optimization: Identical code consolidated to one function.

4.9.1

  • Enhancement: Make it possible to assign instantiator for package private classes, for example com.google.common.collect.RegularImmutableMap. Contributed by @mhmx (Richard Kovacs)

4.9.0

  • Enhancement: AtomicInteger, AtomicLong, and AtomicBoolean are now supported.

4.8.0

  • Enhancement: Added support for specifying the ClassLoader to be used when mapping JSON to Objects. Useful within OSGI and other frameworks where multiple ClassLoaders are involved. @lightcycle
  • JavaDoc has been significantly updated / improved.

4.7.0

  • Bug fix: failing to set a double field when the JSON from the client contained a whole number (e.g. 300) instead of a decimal (e.g. 300.0). @lordvlad
  • Enhancement: when instantiating classes, json-io iterates through constructors until it can find one that works. The order of constructors was non-deterministic. Now the order is public constructors first, then protected, then private.

4.6.0

  • Bug fix: custom write serializers were being cleared in the write() method, not the close() method after full serialization completed. @darmbrust
  • Enhancement: Access increased to public for the pretty-print support apis, tabIn(), tabOut(), and newLine(). @darmbrust

4.5.0

  • Improved read speed.
  • Black-list support for excluding fields. Submitted by @sgandon
  • Pretty-print with support for options. Submitted by @dtracers
  • Ability to use writeObject() API to write the 'body only'. Submitted by @francisu
  • Bug fix: Unclear error sometimes when a class could not be loaded. Submitted by @francisu
  • Enhancement: Provide optional notification of missing field. Submitted by @francisu

4.4.0

  • JsonReader.jsonToMaps() API is no longer recommended (not yet deprecated). These can easily be turned into JsonReader.jsonToJava(json, [(JsonReader.USE_MAPS):true]). The one difference is the return value will match the return value type of the JSON (not always be a Map).

4.3.1

  • Enhancement: Skip null fields. When this flag is set on the JsonWriter optional arguments, fields which have a null value are not written in the JSON output.

4.3.0

  • Double / Float Nan and inifinity are now written as null, per RFC 4627
  • JsonReader.jsonToJava() can now be used to read input into Maps only (as opposed to attempting to create specific Java objects. Using this API allows the return value to support an array [], object, string, double, long, null as opposed to the JsonReader.jsonToMaps() API which forces the return value to be a Map. May deprecate JsonReader.jsonToMaps() in the future.

4.2.1

  • Bug fix: The error message showing any parsing errors put the first character of the message at the end of the message (off by one error on a ring buffer).
  • Parsing exceptions always include the line number and column number (there were a couple of places in the code that did not do this).

4.2.0

  • Enhancement: In Map of Maps mode, all fields are kept, even if they start with @. In the past fields starting with @ were skipped.
  • Ehancement: No longer throws ClassNotFound exception when the class associated to the @type is not found. Instead it returns a LinkedHashMap, which works well in Map of Maps mode. In Object mode, itmay work if the field can have the Map set into it, otherwise an error will be thrown indicating that a Map cannot be set into field of type 'x'.
  • Bug fix: In Map of Maps mode, Object[] were being added with an @items field. The object[] is now stored directly in the field holding it. If an Object[] is 'pointed to' (re-used), then it will be written as an object { } with an @id identifying the object, and an @items field containing the array's elements.

4.1.10

  • Enhancement: Java's EnumSet support added (submitted by @francisu) without need for using custom instantiator.
  • Enhancement: Added support for additional instantiator, ClassFactory2 that takes the Class (c) and the JsonObject which the instance will be filled from. Useful for custom readers.

4.1.9

  • Bug fix: When writing a Map that has all String keys, the keys were not being escaped for quotes (UTF-8 characters in general).

4.1.8

  • Bug fix: 4.1.7 skipped ALL transient fields. If a transient field is listed in the field specifiers map, then it must be traced.

4.1.7

  • Bug fix: Transient fields are skipped during reference tracing. (fix submitted by Francis Upton, @francisu). Some transient fields could cause an exception to be thrown when being trace for references, stopping serialization.

4.1.6

  • Better support for primitive output when 'never show type' is set. (submitted by @KaiHufenbach)

4.1.5

  • Tests updated to use Groovy 2.4.4
  • Deserialization updated to handle objects where the referencing class uses an Object pointer and writes the value out as single primitive value, using the 'value' key. (submitted by @KaiHufenbach)
  • pom filed updated to use a maven bundle plugin (Apache Felix) to generate OSGI headers (submitted by @KaiHufenbach)

4.1.4

  • Bug fix: Custom readers will now always have the .target field set if a JsonObject is passed to them. The custom reader's read() method was being called before the .target field was set on the JsonObject.

4.1.3

  • Made JsonReader / JsonWriter getObjectsReferenced() API public (allows custom reader / writers access to these)
  • Resolver.createJavaObjectInstance(), used to create the correct Java object for a JsonObject peer, no longer calls the .read() API for objects's with custom readers.

4.1.2

  • All objects in the graph are 'traced' (JsonWriter.traceReferences) except references. The code used to not trace fields on objects that were handled by custom writers.

4.1.1

  • JDK 1.6 support - Use of ReflectiveOperationException changed to InvocationTargetException.

4.1.0

  • JDK 1.6 support restored. Keeping 1.6 support for Android developers. Submitted by @kkalisz

4.0.1

  • To prevent @type from being written, set the optional argument JsonWriter.TYPE = false. This is generally not recommended, as the output JSON may not be able to be re-read into Java objects. However, if the JSON is destined for a non-Java system, this can be useful.

4.0.0

  • Custom readers / writers are set now per-instance of JsonReader / JsonWriter, not static. This allows using different customization for cloning, for example, than for serialization to client.
  • JsonReader.jsonToJava() and JsonReader.jsonToMaps() now allow an InputStream to be used.
  • Custom readers / writers can now be set all-at-once through the optional 'args' Map.
  • 'notCustom' readers / writers can now be set all-at-once through the optional 'args' Map.
  • The removeReader(), removeWriter(), removeNotCustomReader(), and removeNotCustomWriter() APIs have been removed since customizers are set per-instance.

3.3.2

  • Added new JsonObject.isReference() API which will return 'true' if the JsonObject is currently representing a reference @ref
  • Added new JsonReader.getRefTarget(jsonObject) API which will follow the @ref links until it resolves to the referenced (target) instance.
  • Added new JsonReader() constructor that only takes the args (Map). It is expected that you will call JsonReader.jsonObjectsToJava(rootJsonObject) which will parse the passed in JsonObject graph.
  • Added new JsonReader.removeReader() API to remove a custom reader association to a given class.
  • Added new JsonWriter.removeWriter() API to remove a custom writer association to a given class.
  • Added new JsonReader.removeNotCustomReader() API to remove a not custom reader - if a notCustom() reader has been added (preventing inherited object from using custom reader), the association can be eliminated using this API.
  • Added new JsonWriter.removeNotCustomWriter() API to remove a not custom writer - if a notCustom() writer has been added (preventing inherited object from using custom writer), the association can be eliminated using this API.

3.3.1

  • Re-entrancy issue fixed. If a CustomReader (or CustomWriter) instantiated another copy of JsonReader or JsonWriter (indirectly, through recursion, for example), the 2nd instance of JsonReader or JsonWriter would clobber the ThreadLocal values inside JsonReader / JsonWriter. Those ThreadLocal values have been removed and converted to per-instance member variables.

3.3.0

  • Consolidate all 3.2.x changes
  • Last snippet read no longer shows 'boxes' for unused internal buffer characters.
  • JsonWriter - moved reference check 'up' to writeImpl() so that each specific 'write' routine did not have to test / call writeOptionalReference().
  • If you have a custom reader that does not bother to resolve references from 'deeper' internal JsonObject maps, an exception will no longer be thrown. It is OK for a custom reader not to 'care' about internal deeper fields if it wants to ignore them.

3.2.3

  • Cache Map's for custom reader's updated to be ConcurrentMap instead of Map.

3.2.2

  • JsonCustomReaderEx added, which passes the 'args' Map through to the custom reader.
  • Both JsonCustomReaderEx and JsonCustomWriterEx have a Map as the last argument in their single method that is implemented by the custom reader / writer. This Map is the same as the 'args' Ma passed into to the JsonReader / JsonWriter, with the added JSON_READER or JSON_WRITER key and associated value of the calling JsonReader / JsonWriter instance.

3.2.1

  • Made Support.getWriter() method public static so that CustomWriters can easily use it
  • Changed JsonCustomWriterEx to no longer inherit from JsonCustomWriter and instead added a common parent (JsonCustomWriterBase). This allows only one method to be overridden to create a JsonCustomWriterEx.

3.2.0

  • New JsonCustomWriterEx interface which adds the JsonWriter access to the implementing class so that it can call back and use jsonWriter.writeImpl() API.
  • Change JsonWriter.writeImpl() from protected to public

3.1.3

  • Performance improvement: No longer using .classForName() inside JsonObject to determine isMap() or isCollection(). Reading JSON into Map of Maps mode significantly faster.

3.1.2

  • Bug fix: Version 3.1.1 introduced a bug where it would always run as though it was in JSON to Java mode always (as opposed to supporting JSON to Maps). This has been fixed.

3.1.1

  • JsonReader.UNKNOWN_OBJECT added as an option to indicate what to do when an unknown object is encountered in the JSON. Default is a Map will be created. However, you can set this argument to a String class name to instantiate, or set it to false to force an exception to be thrown.

3.1.0

*New Feature: Short class names to reduce the size of the output JSON. This allows you to, for example, substitute java.util.HashMap with hmap so that it will appear in the JSON as "@type":"hmap". Pass the substitution map to the JsonWriter (or reader) as an entry in the args Map with the key of JsonWriter.TYPE_NAME_MAP and the value as a Map instance with String class names as the keys and short-names as the values. The same map can be passed to the JsonReader and it will properly read the substituted types. *New Feature: Short meta-key names to reduce the size of the output JSON. The @type key name will be shortened to @t, @id => @i, @ref => @r, @keys => @k, @items => @e. Put a key in the args Map as JsonWriter.SHORT_META_KEYS with the value true.

3.0.2

  • Bug fix: Using a CustomReader in a Collection with at least two identical elements causes an exception (submitted by @KaiHufenbach).

3.0.1

  • Added new flag JsonWriter.WRITE_LONGS_AS_STRINGS which forces long/Long's to be written as Strings. When sending JSON data to a Javascript, longs can lose precision because Javascript only maintains 53-bits of info (Javascript uses IEEE 754 double for numbers). The precision is lost due to some of the bits used for maintaining an exponent. With this flag set, longs will be sent as Strings, however, on return back to a Java server, json-io allows Strings to be set right back into long (fields, array elements, collections, etc.)

3.0.0

  • Performance improvement: caching the custom readers and writes associated to given classes.
  • Ease of use: json-io throws a JsonIoException (unchecked) instead of checked exception IOException. This allows more flexibility in terms of error handling for the user.
  • Code cleanup: Moved reflection related code from JsonReader into separate MetaUtils class.
  • Code cleanup: Moved FastPushbackReader from JsonReader into separate class.
  • Code cleanup: Moved JSON parsing code from JsonReader into separate JsonParser class.
  • Code cleanup: Moved built-in readers from JsonReader to separate Readers class.
  • Code cleanup: Moved resolver code (marshals map of maps to Java instances) into separate Resolver classes.

2.9.4

  • JsonReader.newInstance() API made public
  • Bumped version of junit from 4.11 to 4.12
  • Added additional tests to ensure that null and "" can be properly assigned to primitive values (matching behavior of java-util's Converter.convert() API).

2.9.3

  • Bug fix: When writing a Map with JSON primitive keys (String, Long, Double, or Boolean), a ClassCastException was being thrown if the type was Long, Double, or Boolean. This has been fixed with test added.

2.9.2

  • Android: Rearranged [:.] to [.:] in regular expressions for Android compatibility. Technically, it should not matter, but [:.] was causing java.util.regex.PatternSyntaxException: Syntax error U_ILLEGAL_ARGUMENT_ERROR on Android JVM.
  • Bug fix: When using the JsonWriter arguments Map with FIELD_SPECIFIERS, if you specified a field that was transient, it was not serialized. This has been corrected. When you specify the field list for a given class, the Map can contain any non-static fields in the class, including transient fields.
  • All JUnit tests converted to Groovy.

2.9.1

  • Bug fix: Parameterized types are only internally stamped onto generic Maps (Maps read with no @type) if the field that points to the Map is a template variable or it has template arguments.
  • Performance optimization: tracing references specially handles Collection and Map. By avoiding internal structures, the reference trace is much faster.

2.9.0

  • Unmodifiable Collections and Maps can now be serialized.
  • Added tests to ensure that JsonReader.jsonToMaps() coerces the RHS values when logical primitives, to the optional associated @type's fields.
  • More tests and improved code-coverage.

2.8.1

  • Bug fix: JsonReader.jsonToMaps() API was incorrectly attempting to instantiate peer objects (specified by "@type" field in the JSON) when in 'maps' mode. This made JsonReader.jsonToMaps() fail if all referenced class names did not exist in the JVM. This has been fixed.
  • Minor Javadoc cleanup (Daniel Darabos @darabos)
  • Began migration of tests from one monolithic Java class (TestJsonReaderWriter) to individual Groovy test classes.

2.8.0

  • Additional attempt to instantiate classes via sun.misc.Unsafe added (optional must be turned on by calling JsonReader.setUseUnsafe(true)). json-io already tries all constructors (private or public) with varying arguments, etc. If this fails and unsafe is true, it will try sun.misc.Unsafe.allocateInstance() which effectively does a C-style malloc(). This is OK, because the rest of JsonReader fills in the member variables from the serialized content. (Submitted by @KaiHufenbach).

2.7.6

  • Performance optimizations. Use of switch statement instead of if-else chains.
  • JDK 1.7 for source code and target JVM.

2.7.5

  • Bug fix: ArrayIndexOutOfBounds could still occur when serializing a class with multiple Templated fields. The exception has been fixed.

2.7.4

  • Bug fix: ArrayIndexOutOfBounds exception occurring when serializing non-static inner class with nested template parameters. JsonReader was incorrectly passing on the 'this$0' field for further template argument processing when it should not have.

2.7.3

  • JsonReader executes faster (more efficiently manages internal 'snippet' buffer and last line and column read.)
  • Improved date parsing: day of week support (long or short name), days with suffix (3rd, 25th, etc.), Java's default .toString() output for Date now parses, full time zone support, extra whitespace allowed within the date string.
  • Added ability to have custom JSON writers for interfaces (submitted by @KaiHufenbach).

2.7.2

  • When writing JSON, less memory is used to manage referenced objects. JsonWriter requires a smaller memory foot print during writing.
  • New option available to JsonWriter that allows you to force enums to not write private variables. First you can make them transient. However, if you do not own the code or cannot change it, you can set the JsonWriter.getArgs().put(ENUM_PUBLIC_ONLY, true), and then only public fields on enums will be emitted.

2.7.1

  • BigDecimal and BigInteger are now always written as a primitive (immutable, non-referenced) value. This uniformizes their output.

2.7.0

  • Updated to support JSON root of String, Integer, Floating point, and Boolean, per the updated JSON RFP. Example, the String "football" is considered valid JSON. The JsonReader.readObject() API and JsonReader.jsonToJava() will return a String in this case. The JsonReader.jsonToMaps() API will still return a Map (JsonObject), and the @items key will contain an Object[] with the single value (String, Integer, Double, Boolean) in it.
  • When a Java Map has only String keys in it, json-io will use the JSON object keys directly and associate the values to the keys as expected. For example, the Map ['Football':true] would be written {"Football":true}. However, if the keys are non-Strings, then Maps will be written as a JSON object with {"@keys":[...], "@items":[...]}, where @keys is an array [] of all the keys, and the @items is an array [] of all the values. Entry 0 of @keys matches with Entry 0 in the @items array, and so on. Thanks for Christian Reuschling for making the request and then supplying the implementation.
  • Change some APIs from private to protected to allow for subclasses to more easily override the default behavior.

2.6.1

  • Bug fix: An internal Map that kept meta-information about a Java Class, changed to ConcurrentHashMap from HashMap.

2.6.0

  • Added support for specifying which fields on a class will be serialized. Use the JsonWriter.FIELD_SPECIFIERS key and assign the value to a Map<Class, List<String>>, where the keys of the Map are classes (e.g. Bingo.class) and the values are List<String>, which indicates the fields to serialize for the class. This provides a way to reduce the number of fields written for a given class. For example, you may encounter a 3rd Party class which fails to serialize because it has an oddball field like a ClassLoader reference as a non-static, non-transient field. You may not have access to the source code to mark the field as transient. In this case, add the appropriate entries in the FIELD_SPECIFIERS map. Voila, problem solved. Use the JsonWriter API that takes optionalArgs Map. The key for this Map is JsonWriter.FIELD_SPECIFIER and the value is Map<Class, List<String>>.

2.5.2

  • java.net.URL can now be used as a constructor argument. The reader was throwing an exception instantiating a constructor with a URL parameter.
  • java.lang.Object parameters in constructor arguments are now tried with both null and new Object() now.

2.5.1

  • Fixed a bug (introduced in 2.5.0) in the processing of a Map that has a Collection as a key.

2.5.0

  • New 'Pretty-Print' option available. If the 'args' Map passed to JsonWriter.objectToJson(o, args) contains the key JsonWriter.PRETTY_PRINT and the value 'true' (boolean or String), the JsonWriter output will be formatted in a nice human readable format.
  • Convert a JSON String to Pretty-Print format using JsonWriter.formatJson(String json). A String will be returned with the JSON formatted in a nice, human readable format.
  • If a Field contains Parameterized types (e.g., Map<String, Set<Long>>, and so on), JsonReader will use those fields to process objects deep within Maps, Collections, etc. and still create the proper Java class.

2.4.5

  • Allow "" to be set into Date field, setting the Date field (or Date array element) as null.

2.4.4

  • Allow "" to be set into BigInteger or BigDecimal when return value is Map (JsonObject). "" to non-String fields will be null, except for primitives and primitive wrappers, that will result in JVM default value.

2.4.2

  • Allow "" to be set into non-String fields, when doing so, null is set on Object type fields; on primitive fields, the JVM default value is set. This is for when converting JSON to Java objects directly.

2.4.1

  • Added support to allow primitives and String to be assigned to abstract / interface / base type field on an object (Serializable, Comparable, Object, etc.). Primitives can now be 'set' into these fields, without any additional type information.

2.4.0

  • Primitives can be set from Strings
  • Strings can be set from primitives
  • BigDecimal and BigInteger can be set from primitives, Strings, BigDecimal, or BigInteger

2.3.0

  • Maps and Collections (Lists, Set, etc.) can be read in, even when there are no @keys or @items as would come from a Javascript client. *json-io will now use the generic info on a Map<Foo, Bar> or Collection<Foo> object's field when the @type information is not included.json-io will then know to create Foo instances, Bar instances, etc. within the Collection or Map.
  • All parsing error messages now output the last 100 characters read, making it easier to locate the problem in JSON text. Furthermore, line and column number are now included (before it was a single position number). This allows you to immediately find the offending location.
  • You can now force @type to be written (not recommended) by putting the JsonWriter.TYPE key in the JsonWriter args map, and assigning the associated value to true.

2.2.32

  • Date/Time format can be customized when writing JSON output. New optional Map args parameter added to main API of JsonWriter that specifies additional parameters for JsonWriter. Set the key to JsonWriter.DATE_FORMAT and the value to a SimpleDateFormat string. Two ISO formats are available for convenience as constants on JsonWriter, JsonWriter.ISO_DATE_FORMAT and JsonWriter.ISO_DATE_TIME_FORMAT.
  • JsonReader updated to read many different date/time formats.
  • When JsonReader encounters a class that cannot be constructed, you can associate a ClassFactory to the class, so that then the un-instantiable class is encountered, your factory class will be called to create the class. New API: JsonReader.assignInstantiator(Class c, ClassFactory factory)

2.2.31

  • Adds ability to instantiate a wider range of constructors. This was done by attempting construction with both null and non-null values for many common class types (Collections, String, Date, Timezone, etc.)

2.2.30

  • java.sql.Date when read in, was instantiated as a java.util.Date. This has been corrected.

2.2.29

  • First official release through Maven Central