- PERFORMANCE:
JsonWriter.writeObjectArray()now fast-pathsBoolean,Double,Long,Integer,Float,Short,Byte, andStringelements directly towritePrimitive()/writeStringValue(), bypassing thewriteImpl()→writeCustom()dispatch chain when@typeis not needed. Mirrors the existingwriteCollectionElement()optimization. JFR showswriteArrayElementIfMatchingdropped from 62 samples to 0.
- BUG FIX:
ToonWriter— nested collections and arrays with type metadata (showTypeInfoAlways()) now emit properly indented$type/$itemsblocks. Previously, the compactfieldName[N]:path bypassedwriteCollection()/writeArray()entirely, and the inline list-element path wrote$type/$itemsat incorrect indentation levels. Fixed inwriteFieldEntry(),writeFieldEntryInline(),writeFoldedEntry(), andwriteListElement(). - BUG FIX:
ToonWriter—char[]fields now serialize as a plain string value instead of comma-separated characters, matching the Converter'sString → char[]read path for correct round-trips. - BUG FIX:
ToonWriter— maps with complex keys (e.g., aHashMapused as a key in anotherHashMap) no longer causeStackOverflowError.writeListElement()now checkshasComplexKeys()and routes towriteMap()(which uses@keys/@valuesformat with cycle detection) instead ofwriteMapInline()which calledkey.toString()unconditionally. - BUG FIX:
EnumSetFactory— now infers the enum element type from the field's generic declaration (e.g.,EnumSet<Color>→Color.class) viaJsonObject.getItemElementType(). Previously, empty EnumSets or EnumSets without explicit@typeon items could not round-trip through JSON/TOON when the field provided generic type information. LegacyRegularEnumSetJSON without@itemsnow deserializes to an emptyEnumSetinstead ofnullwhen 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>, andEnumSet<Color>fields all round-trip correctly using generic type inference fromseedIncrementalContainerMetadata()— no explicit@type/$typeneeded in the stream. - IMPROVEMENT:
JsonObjectinternal 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 cachedeffectiveValuesreference 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
$typemetadata. 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 tocycleSupport(false)whennullis passed forWriteOptions, skipping thetraceReferences()pre-pass for ~35-40% faster TOON serialization. TOON targets LLM communication where data is typically acyclic; if a cycle is encountered, a clearJsonIoExceptionis thrown with guidance to enablecycleSupport(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:
ToonReadernow 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
FastReadercharacter buffer viaJsonIo's scoped buffer recycler, reducing per-parse reader-buffer allocation without sharing mutable buffers across live parsers. - PERFORMANCE:
ToonReadernow parses common unquoted scalar values directly from source ranges in object, list, inline-array, and tabular read paths, avoiding intermediateStringcreation before boolean/number classification. - PERFORMANCE:
ToonReadernow parses combinedfield[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:
JsonObjectdefault constructor now deferskeys[]/values[]allocation using a shared empty sentinel, avoiding 256 bytes of wasted arrays for array/collection nodes that only useitems[]storage. - PERFORMANCE:
JsonObject.appendFieldForParser()no longer redundantly nullshashandindexfields that are already null during parsing. - PERFORMANCE:
ToonWritertabular POJO detection now validates uniformity directly viaWriteFieldPlanaccessors instead of creating aLinkedHashMapper element viagetObjectFields(). Row values are streamed from accessors during the write phase, eliminating N intermediate map allocations per tabular array. - PERFORMANCE:
JsonWritertracking structures (objVisited,objsReferenced,traceDepthsvsactivePath) are now allocated lazily based oncycleSupportmode, avoiding ~6 KB of wasted allocations per writer when cycle support is disabled. - PERFORMANCE:
JsonWriter.writeCollectionElement()now fast-pathsInteger,Float,Short, andBytedirectly towritePrimitive(), bypassing thewriteImpl()→writeCustom()dispatch chain when@typeis not needed. - PERFORMANCE:
JsonWriter.writeCollection()uses indexedlist.get(i)forRandomAccesslists (e.g.,ArrayList) instead of allocating anIterator.writeObject()and enum field loops also use indexed iteration to avoid implicitIteratorallocation. - FEATURE:
@IoShowType— new field-level annotation (26th annotation) that forces@type/$typeemission on the annotated field's value and its elements (Collections, Maps, arrays), regardless of the globalshowTypeInfosetting. Essential for polymorphic fields when usingshowTypeInfoNever(), JSON5 mode, or TOON format. Jackson's@JsonTypeInfoon a field is honored as a fallback synonym. - IMPROVEMENT:
.json5()onWriteOptionsBuildernow setsshowTypeInfoNever()andcycleSupport(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
toonWriteOptionsbean withcycleSupport(false)for TOON/JSON5 formats, in addition to the primaryjsonIoWriteOptionsbean. - 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.
- PERFORMANCE:
ToonReader.peekLine()now avoids materializing aStringfor blank, comment, and indent-only lines, reducing per-lineStringallocations 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:
JsonObjectindex build is now deferred to the first lookup instead of construction time;ToonReader.lineBuflocalized 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;stringCachearray localized to a local variable in hot parsing methods. - PERFORMANCE:
ToonReadercache arrays (stringCache,numberCacheKeys,numberCacheValues) now reused across parse calls viaThreadLocal, 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-coreandjackson-annotationsdeclarations (pulled transitively viajackson-databind).
- FEATURE: Spring Boot starter — expanded YAML configuration properties. All commonly used
WriteOptionsandReadOptionssettings are now configurable viaapplication.ymlwithout 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 throughReadOptionsCustomizer/WriteOptionsCustomizerbeans. - IMPROVEMENT:
@typeelimination on write now considers@IoDeserialize(as=X)and@IoTypeInfo(X)annotations. When the runtime type of a field matches the annotation-specified type,@typeis 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:
AnnotationResolvernow usesClassUtilities.forName()instead ofClass.forName()for external annotation detection, ensuring proper classloader resolution in OSGi and JPMS environments. - FEATURE: ToonWriter now uses the same
WriteFieldPlan/Accessorabstraction as JsonWriter. This gives TOON serialization automatic support for@IoGetter,@IoFormat,@IoAnyGetter, and all other write-side annotations. Previously ToonWriter used directField.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 incom.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@IoPropertyon 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).@IoPropertyon 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@typeis present in JSON (fallback hint for polymorphic fields).@IoDeserialize(as = LinkedList.class)— field-level or class-level forced type override during deserialization. Priority:@typein JSON >@IoDeserialize>@IoTypeInfo> declared field type.@IoClassFactory(MyFactory.class)— class-level annotation specifying aClassFactoryimplementation for custom deserialization. Factory instances are cached and shared. ProgrammaticaddClassFactory()takes priority.@IoGetter("fieldName")— method-level annotation marking a no-arg instance method as the getter for a field during serialization. Replaces the standardgetXxx()convention. ProgrammaticaddNonStandardGetter()takes priority.@IoSetter("fieldName")— method-level annotation marking a 1-arg instance method as the setter for a field during deserialization. Replaces the standardsetXxx()convention. ProgrammaticaddPermanentNonStandardSetter()takes priority.@IoNonReferenceable— class-level annotation marking a type as non-referenceable. Instances will never emit@id/@refpairs. Annotation equivalent ofconfig/nonRefs.txtandaddNonReferenceableClass().@IoNotCustomReader— class-level annotation that suppresses custom reader usage for this type, even if a custom reader exists for a parent class. Annotation equivalent ofconfig/notCustomRead.txtandaddNotCustomReaderClass().@IoNotCustomWritten— class-level annotation that suppresses custom writer usage for this type, even if a custom writer exists for a parent class. Annotation equivalent ofconfig/notCustomWritten.txtandaddNotCustomWrittenClass().@IoCustomWriter(MyWriter.class)— class-level annotation specifying aJsonClassWriterimplementation for custom serialization. Writer instances are cached and shared. ProgrammaticaddCustomWrittenClass()takes priority.@IoCustomReader(MyReader.class)— class-level annotation specifying aJsonClassReaderimplementation for custom deserialization. Reader instances are cached and shared. ProgrammaticaddCustomReaderClass()takes priority.@IoTypeName("ShortName")— class-level annotation assigning a short alias for@typein JSON. Equivalent ofaliases.txtandaliasTypeName(). On write, emits the alias instead of the FQCN; on read, resolves the alias back to the class. ProgrammaticaliasTypeName()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 globalMissingFieldHandler. Equivalent to Jackson's@JsonAnySetter.@IoAnyGetter— method-level annotation marking a no-arg instance method returningMap<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-styleString.format()patterns containing%(e.g.,"%,d","%.2f","%05d","%x","%10s") — works on any type; (2)DecimalFormatpatterns (e.g.,"#,###","$#,##0.00","0.00") — for numeric types; (3)DateTimeFormatter/SimpleDateFormatpatterns (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. SupportsString,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 aClassValueMap. 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:
Injectornumeric kind and primitive-wrapper lookups now useClassValueMapO(1) dispatch instead of sequentialclass ==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 reusesFastByteArrayOutputStreamandFastWriterbuffers.toJava(String, ...)andtoJava(InputStream, ...)builder paths now reuseFastReaderchar/pushback buffers. This removes repeated stream/buffer construction churn in benchmark loops and reduces allocation pressure.
- PERFORMANCE:
JsonObject.appendFieldForParser()—JsonParsernow uses a parser-only fast path for object field insertion, avoidingJsonObject.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 inskipWhitespaceRead()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 forDOUBLEmode, bypassing minimal-number analysis in the default path. ObjectResolver.traverseArray()now has a primitive-array direct assignment path for parsed JSONLong/Doublevalues, avoiding intermediate wrapper coercion in hot loops.
- Floating-point parsing now takes a direct
- 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 clearsobjVisited/objsReferencedin afinallyblock, 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:
JsonWriternow enforces explicit cycle policy by option:cycleSupport(false)throwsJsonIoExceptionon cyclic graphs with a remediation hint to enablecycleSupport(true), whilecycleSupport(true)retains normal@id/@refhandling. - PERFORMANCE:
JsonWriter— string escaping/writing hot paths optimized: removedString.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 sharedDefaultWriteOptionscaches (usingClassValueMap):- Added per-declared-class gate cache for
isNotCustomWrittenClassand declared custom writer resolution. - Runtime custom writer resolution now reuses the existing
getCustomWriter()/writerCachepath after gate pass (no duplicate runtime cache layer). This lowers hot-loop cost inwriteUsingCustomWriter()/getCustomWriterIfAllowed(), avoids per-JsonWritercache construction churn in short-lived writer workloads, and removes redundant runtime cache indirection.
- Added per-declared-class gate cache for
- PERFORMANCE:
JsonWriter— now consistently honorscycleSupport(false)by bypassingobjsReferenced/@idlookup work across write hot paths (object/map/collection/array/custom writer) while using lightweight active-path checks for cycle detection. - PERFORMANCE:
JsonWriter.writeMapBody(...)(bothIteratorandJsonObjectpaths) 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-redundantfastPrimitiveCoercion(...)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 asJsonObjectinstead of resolving to target types. - BUG FIX:
ObjectResolver.processJsonObjectElement()now preserves explicit element@typemetadata 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 deepmarkUntypedObjects()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 useconverter.isConversionSupportedFor(source, target)in bothassignField()andreadWithFactoryIfExists()(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 unusedcollectionClassparameter and eliminating per-nested-arrayArrayListcopy allocation inhandleArrayInCollection(). Nested arrays now reuse the existingJsonObjectwrapper for traversal, reducing allocation churn in deep generic collection payloads. - BUG FIX:
ObjectResolver.assignField()now consistently applies@IoDeserialize/@IoTypeInfofield overrides when TOON list-style arrays/maps arrive asCollectionvalues, 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()andReadOptionsBuilder.useLegacyMarkUntypedObjectsPrepass(boolean)). - PERFORMANCE:
ReadOptionsBuilder— added cached read injector planning (InjectorPlan) and wiredObjectResolver/Resolverfield 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 constructingJsonIoException(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 internalResolver.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 inResolver,ObjectResolver, andMapResolver. - MAINTENANCE:
Resolver.isPseudoPrimitive(Class<?>)refined to align with read-side semantics by checkingisSimpleTypeConversionSupported(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@typemetadata. - PERFORMANCE:
Resolver— read-side scalar coercion hot paths refactored to avoid legacycoerceLong()/coerceDouble()indirection:- Added target-kind based scalar coercion (
scalarTargetKind/fastScalarCoercion) inResolver. ObjectResolver/MapResolvernow 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.
- Added target-kind based scalar coercion (
- BUG FIX:
ToonWriternow applies@IoPropertyrename and@IoPropertyOrderreordering in itsgetObjectFields()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 viaWriteOptionsBuilder.toonDelimiter(char)per-instance orWriteOptionsBuilder.addPermanentToonDelimiter(char)for JVM-wide defaults. The delimiter is encoded in the count bracket ([N],[N\t],[N|]) so theToonReaderauto-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. WhenprettyPrintisfalse(default), uniform POJO lists/arrays are written in compact CSV-style tabular format ([N]{field1,field2,...}: row1 row2 ...). WhenprettyPrintistrue, the verbose list format (- key: value) is used instead. - FEATURE:
ToonWriternow defaults TOON metadata keys to$variants ($id/$ref/$items/$keys) for JSON5-friendly output.useMetaPrefixAt()anduseMetaPrefixDollar()overrides are still honored. - FEATURE:
ToonWriternow emits TOON type metadata (@type/@t/$type/$t) when type output is enabled (showTypeInfoMinimal(),showTypeInfoMinimalPlus(), orshowTypeInfoAlways()), including nested field values, arrays, collections, maps, map keys, and map values. - BEHAVIOR:
JsonIo.toToon(..., null)now defaults TOON writing toshowTypeInfoNever()so type metadata is omitted unless explicitly enabled viaWriteOptions. - BUG FIX:
ToonWriternow enforces explicit cycle policy by option:cycleSupport(false)throwsJsonIoExceptionon cyclic graphs, whilecycleSupport(true)emits TOON id/ref metadata for shared references and cycles (including arrays, collections, maps, and POJOs) to preserve object identity. - BUG FIX:
ToonWritercycle exceptions incycleSupport(false)mode now include a direct remediation hint to enablecycleSupport(true)for cyclic graph serialization. - BUG FIX:
ToonWriternow indents nested referenced-map id metadata correctly (including complex-key maps), preventing metadata from being parsed at the wrong level. - BUG FIX:
ToonReadernow recognizes TOON metadata keys (@id/@ref/@items/@keysplus 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,@IoTypeInfofield 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 methodwriteCollectionElements(Collection<?>). - PERFORMANCE:
WriteOptionsBuilder— added cached write-field planning (WriteFieldPlan) and switchedJsonWriterobject/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
PolymorphicArrayElementTypeTestto verify explicit array element@typeis 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 debugSystem.out.printlncalls; all 33 tests and assertions retained.SkippedConversionsDebugTest: Replaced 60+ debugprintlncalls with proper JUnit assertions; all 12 tests retained.AutomaticColorTest,AutomaticFileTest,AutomaticPathTest: Removed verboseLogger/System.out.printlndebug output.Issue423UnknownTypeTest,Issue425ExactReproTest,Issue425NestedJsonTest: Removed verbose debug output.SecurityAuditLoggerTest: Fixed log handler lifecycle to suppress console output during tests.
- BUG FIX: Updated
java-utildependency to4.96.0-SNAPSHOTwhich fixes aClassUtilities.trySetAccessible()caching bug. TheWeakHashMap-based accessibility cache usedequals()lookup, causing differentFieldinstances for the same logical field to share cache entries. This left fields inaccessible, causingTraverserto silently skip them andGraphComparator.applyDelta()to fail with "source object not found." - TEST FIX:
EnumTests— updatedtestDuplicateRefandtestEnumFieldto expect successful deserialization of non-static inner classes of package-private outer classes. These classes are correctly instantiable viasetAccessible()on classpath (no JPMS); the previous test expectations relied on the now-fixed caching bug.
- FEATURE: Added TOON strict-read configuration to
ReadOptions/ReadOptionsBuilder:- New
ReadOptions.isStrictToon()getter. - New
ReadOptionsBuilder.strictToon()andReadOptionsBuilder.strictToon(boolean)setters. ToonReadernow enforces strict-mode validation (indentation, array counts, blank lines in arrays, delimiter/header consistency) when enabled.- Copy-constructor propagation and builder tests added.
- New
- TESTING: Added
ToonSpecDecodeFixturesTestto run officialtoon-format/specdecode fixtures againstJsonIo.fromToon()(opt-in viaRUN_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) inResolver. ObjectResolver/MapResolvernow 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.
- Added target-kind based scalar coercion (
- PERFORMANCE:
JsonWriter.writeMapBody(...)(bothIteratorandJsonObjectpaths) 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:
JsonParsernow uses a parser-onlyJsonObject.appendFieldForParser()fast path for object field insertion, avoidingJsonObject.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
JsonIoString-based JSON paths:toJson(Object, WriteOptions)now reusesFastByteArrayOutputStreamandFastWriterbuffers.toJava(String, ...)andtoJava(InputStream, ...)builder paths now reuseFastReaderchar/pushback buffers. This removes repeated stream/buffer construction churn in benchmark loops and reduces allocation pressure.
- PERFORMANCE: Read numeric pipeline optimized:
JsonParserfloating-point parsing now takes a directDouble.parseDouble()fast path when configured forDOUBLEmode, bypassing minimal-number analysis in the default path.ObjectResolver.traverseArray()now has a primitive-array direct assignment path for parsed JSONLong/Doublevalues, avoiding intermediate wrapper coercion in hot loops.
- PERFORMANCE:
JsonWritercustom-writer gate checks are now class-cached via sharedDefaultWriteOptionscaches (usingClassValueMap):- Added per-declared-class gate cache for
isNotCustomWrittenClassand declared custom writer resolution. - Runtime custom writer resolution now reuses the existing
getCustomWriter()/writerCachepath after gate pass (no duplicate runtime cache layer). This lowers hot-loop cost inwriteUsingCustomWriter()/getCustomWriterIfAllowed(), avoids per-JsonWritercache construction churn in short-lived writer workloads, and removes redundant runtime cache indirection.
- Added per-declared-class gate cache for
- PERFORMANCE:
JsonWriternow consistently honorscycleSupport(false)by bypassingobjsReferenced/@idlookup 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) inWriteOptionsBuilderand switchedJsonWriterobject/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) inReadOptionsBuilderand wiredObjectResolver/Resolverfield 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:
ObjectResolvergeneric inference now runs incrementally (on-demand) and no longer relies on the deepmarkUntypedObjects()pre-pass. This reduces upfront traversal work while preserving nested generic correctness across parameterized object fields, collections, and maps. - BUG FIX:
ObjectResolvermap generic typing now preserves and applies both key and value generic types during traversal, fixing cases where complex map keys/values could remain asJsonObjectinstead of resolving to target types. - CLEANUP: Removed legacy pre-pass code path and associated temporary API toggles (
ReadOptions.useLegacyMarkUntypedObjectsPrepass()andReadOptionsBuilder.useLegacyMarkUntypedObjectsPrepass(boolean)). - BUG FIX:
JsonWriter.write()now always clearsobjVisited/objsReferencedin afinallyblock, 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:
JsonWriterstring escaping/writing hot paths optimized: removedString.format("\\u%04x", ...)from control-char escaping, added precomputed control escape strings, and unified smart-quote decision logic to avoid duplicated scanning code. - PERFORMANCE:
JsonParserhot 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 inResolver,ObjectResolver, andMapResolver. - MAINTENANCE: Refined
Resolver.isPseudoPrimitive(Class<?>)to align with read-side semantics by checkingisSimpleTypeConversionSupported(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:
ObjectResolverscalar fast-path conversion gates now useconverter.isConversionSupportedFor(source, target)in bothassignField()andreadWithFactoryIfExists()(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@typemetadata 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
PolymorphicArrayElementTypeTestto verify explicit array element@typeis honored over declared component type inference. - BUG FIX:
Resolver.wrapException()now preserves the causal chain by constructingJsonIoException(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:
ObjectResolvernested collection type-marking path was cleaned up by removing an unusedcollectionClassparameter and eliminating per-nested-arrayArrayListcopy allocation inhandleArrayInCollection(). Nested arrays now reuse the existingJsonObjectwrapper 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.
- PERFORMANCE:
JsonWriter- Localizedthis.outto 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 preferprivateLookupIn-based LambdaMetafactory first (nosetAccessibleneeded), demotingsetAccessible(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.
- PERFORMANCE: Optimized
Resolver/ObjectResolverread path (~6% faster Java deserialization)- Correctness fix:
createSameTypeCollection()- Fixed inheritance-order bug whereLinkedHashSetwas incorrectly created asHashSet, losing insertion-order preservation - Correctness fix:
shouldSkipTraversal()- FixedrawClass.isInstance(Number.class)(always false) toNumber.class.isAssignableFrom(rawClass) - Performance: Eliminated strategy pattern in
assignField()- removedAssignmentStrategyinterface,AssignmentContextclass, 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 expensiveConverterlookup chains - Performance:
Injectornow usesLambdaMetafactory-generatedBiConsumerfor field injection (~5-8% faster Read). The JIT can inline the generated lambda to near-direct field access speed, replacing non-inlinableMethodHandle/VarHandleinstance-field dispatch. UsesprivateLookupIn(JDK 9+) for non-public classes; falls back gracefully to existing mechanisms. - Performance:
Accessornow usesLambdaMetafactory-generatedFunctionfor field access during JSON writing. Same JIT-inlinable technique asInjector, applied to the write path.
- Correctness fix:
- BUG FIX:
JsonWriter.doesValueTypeMatchFieldType()-declaredClassIsLongWrittenAsStringincorrectly checkedobjectClassinstead ofdeclaredTypefor thelong.classcase, causing unnecessary@typewrappers on primitive long fields whenwriteLongsAsStringswas enabled - BUG FIX:
JsonWriter.doesValueTypeMatchFieldType()-Long.classvslong.classautoboxing mismatch caused unnecessary@typewrappers onLongvalues assigned to primitivelongfields - 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:ToonWriternow writes empty maps inline as{}in both standalone and list-element contexts;ToonReadernow recognizes{}in list arrays as empty objects instead of treating them as strings - BUG FIX:
ToonReader.parseNumber()- Replaced custom numeric parsing with delegation toMathUtilities.parseToMinimalNumericType()from java-util, fixing extreme doubles (e.g.,Double.MAX_VALUEwritten as 309-digit plain integer) and largeBigIntegervalues that were incorrectly returned as strings - BUG FIX:
ToonWriter- Fixed double-colon bug in nested collections:writeCollection()was writing[N]:then callingwriteCollectionElements()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-arraygetCustomWriter()lookups - PERFORMANCE:
JsonWriter- AddedwriteIntDirect()for zero-allocation int/short writing using digit-pair lookup tables, replacingInteger.toString()+Writer.write(String) - PERFORMANCE:
JsonWriter- CachedEnumSet.elementTypefield reflectively (lazy-initialized volatile) to avoid repeatedgetDeepDeclaredFields()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}
- Writer: Added
- 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
- Added support for tab-separated tabular format:
- TESTING: Comprehensive TOON test suite — 241 tests total across
ToonReaderTestandToonWriterTest- 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
- MAINTENANCE: Migrated test files from deprecated
JsonIo.toObjects()toJsonIo.toJava().asClass()API- Updated ~148 calls across 5 test files to use the new fluent builder pattern
- Deprecated
toObjects()methods remain available inJsonIo.javafor backward compatibility
- REFACTOR: Consolidated duplicate parse/resolve logic in
JsonIobuilder 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
- Extracted common
- 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
- Uses new
- PERFORMANCE: Optimized
JsonWriter.writeCustom()to avoid redundantgetCustomWriterlookups- Combined
isCustomWrittenClasscheck with writer lookup into singlegetCustomWriterIfAllowedmethod - When declared type equals runtime type (common case), only one
getCustomWritercall instead of two
- Combined
- 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
- Pre-computed
- SPRING: Default
show-type-infochanged fromMINIMALtoMINIMAL_PLUS- Spring Boot starter now uses
MINIMAL_PLUSas the default for optimal JSON size - Added
MINIMAL_PLUSoption to Spring configuration properties
- Spring Boot starter now uses
- IMPROVED: Numeric primitive simplification in
MINIMAL_PLUSandNEVERtype info modesByte,Short,Integer, andFloatvalues now write as plain JSON numbers when the declared type isObject- Applies to fields declared as
Object, collection elements in raw collections, andMap<String, Object>values - These types round-trip as
Long(for integers) orDouble(for floats) when read back - Reduces JSON size by eliminating
{"@type":"Integer","value":42}wrappers in favor of plain42 - Does not apply to
MINIMALorALWAYSmodes to maintain backward compatibility
- IMPROVED:
MapResolver- Applied consistency improvements from ObjectResolver patterns- Added
isFinishedguard totraverseMap()to prevent reprocessing - Updated
traverseArray()andtraverseCollection()to usemarkFinishedIfNot()helper - Removed redundant
setFinished()calls at method ends (now handled by guard pattern)
- Added
- BUILD: Fixed
json-io-spring-boot-starterMaven 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
- 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
HttpMessageConvertersupport - Spring MVC HttpMessageConverters for content negotiation:
JsonIoHttpMessageConverter-application/jsonJson5HttpMessageConverter-application/vnd.json5ToonHttpMessageConverter-application/vnd.toon
- WebFlux Encoders/Decoders for reactive applications:
JsonIoEncoder/JsonIoDecoder-application/jsonJson5Encoder/Json5Decoder-application/vnd.json5ToonEncoder/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)
- Write options:
- Customizer interfaces for programmatic configuration:
ReadOptionsCustomizer- Customize read behaviorWriteOptionsCustomizer- Customize write behavior
- Jackson coexistence modes:
COEXIST(default) - json-io handles JSON5/TOON, Jackson handles JSONREPLACE- 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
- 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
WriteOptionsexcluded/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
- Primitives and wrappers:
- Added
- 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: valueobject syntax with nested structure detection- Inline arrays:
[N]: elem1,elem2,elem3 - List format arrays:
[N]:followed by- elemlines - 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 typeMap<String, Person>- Map values converted to target typeMap<Person, String>- Maps with complex object keys (uses$key/$valuearray-of-entries format)
- Added
- FEATURE:
WriteOptionsBuilder- AddedcycleSupport(boolean)option for performance optimizationcycleSupport(true)(default) - Full cycle support with@id/@reffor multi-referenced objectscycleSupport(false)- Skips thetraceReferences()pre-pass for faster serialization of acyclic data- When
cycleSupport=falseand 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
@JsonIdentityInfoannotation, throws exception if cycles not handled - GSON: No built-in cycle support, throws
StackOverflowErroron 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
- Jackson: Requires
- Added
isCycleSupport()getter toWriteOptions - Added
addPermanentCycleSupport(boolean)for application-scoped configuration
- BUILD: Updated java-util dependency from 4.84.0 to 4.85.0
- FEATURE:
WriteOptionsBuilder- Added meta key prefix override methodsuseMetaPrefixAt()- Forces@prefix for all meta keys (@type,@id,@ref, etc.) even in JSON5 modeuseMetaPrefixDollar()- Forces$prefix for all meta keys ($type,$id,$ref, etc.) even in standard JSON modegetMetaPrefixOverride()- Returns the override character (@,$, ornullfor 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
- BUG FIX:
JsonWriter.writeJsonObjectObject()- Fixed JSON5 unquoted keys not being applied- When serializing
JsonObjectinstances directly, thejson5UnquotedKeysoption 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
- When serializing
- PERFORMANCE:
JsonWriter- Comprehensive hot path optimization- Hoisted 12
WriteOptionsvalues 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()andwriteElements()loops to write comma BEFORE entries (except first), eliminating doublehasNext()check per iteration - Replaced all internal
getWriteOptions()calls with direct field access - Optimized
tab()method to use pre-fetched indentation settings
- Hoisted 12
- 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
- Replaced
- PERFORMANCE:
JsonWriter- NewIdentityIntMapfor reference tracking- Replaced
IdentityHashMap<Object, Long>with lightweightIdentityIntMap - Uses open addressing with primitive int values (no Entry objects, no boxing)
- Single
System.identityHashCode()call per operation - ~10% improvement in write performance
- Replaced
- PERFORMANCE:
ObjectResolver.markUntypedObjects()- Optimized visited set- Now uses
IdentitySetfrom java-util (high-performance Set using object identity) - Previously used workaround with
IdentityIntMap, now uses properadd()/contains()semantics - 66% reduction in samples for this method, eliminated IdentityHashMap allocations
- Now uses
- BUG FIX:
ReadOptionsBuilder.integerTypeBigInteger()- Fixed to actually set BigInteger-only mode- Method was incorrectly setting
Integers.BOTHinstead ofIntegers.BIG_INTEGER - Now correctly forces all integers to return as BigInteger regardless of size
- Method was incorrectly setting
- TESTING: Added comprehensive
JsonParserErrorHandlingTestwith 52 tests for parser edge cases@itemsvalidation 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,@enumvalidation (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
- When parsing
- PERFORMANCE:
JsonParser.readJsonObject()- Skip type resolution for simple Class types- When
suggestedTypeisnullor already aClass, skip the redundantTypeUtilities.resolveType()call - Only
ParameterizedTypeand other complex types need resolution against themselves - This is the most common case in parsing, reducing overhead in the hot path
- When
- BUG FIX:
ObjectResolver- Fixed deserialization of arrays containing Collections- Fields declared as
List<String>[],ArrayList<Integer>[], etc. now deserialize correctly - Previously,
resolveArray()incorrectly calledcreateAndPopulateCollection()for Collection element types, creating a single Collection instead of an array of Collections - Added handling in
createAndPopulateArray()to convert innerObject[]to Collection instances when the component type is a Collection - Also fixed
assignField()to useinstanceof Object[]check instead ofisArray()to avoid ClassCastException with primitive arrays - Removed unused
createAndPopulateCollection()method
- Fields declared as
- 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
- Previously
- REFACTOR:
ObjectResolver- Extracted shared array element processing helpers- Eliminated code duplication between
traverseArray()andcreateAndPopulateArray() - 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
- Eliminated code duplication between
- TESTING: Added
MapResolverGenericArrayTestwith 13 tests for MapResolver coverageGenericArrayTypehandling ingetUltimateComponentType()- Tests parsing of generic array types likeList<String>[]in Maps modereconcileResult()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
- REMOVED:
ReadOptions.getMaxEnumNameLength()and related builder methods- Removed
maxEnumNameLengthsecurity limit option - enum name length is already bounded by Java class loading - Removed
ReadOptionsBuilder.maxEnumNameLength()andaddPermanentMaxEnumNameLength()methods - Simplifies the API by removing unnecessary security option
- Removed
- PERFORMANCE:
Resolver- Hoisted additional ReadOptions constants- Pre-fetched
maxStackDepth,maxMapsToRehash, andreturningJavaObjectsto final fields - Eliminates repeated method calls in hot paths during JSON parsing
- Pre-fetched
BUG FIX: Resolver - Fixed array/collection cross-conversion returning null
- When JSON contained
@type=char[]but caller requestedbyte[].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()andshowRootTypeInfo()toWriteOptionsBuilder- Control whether
@typeis written on the root object when usingshowTypeInfoMinimal()(the default) omitRootTypeInfo()- Omit the@typeon the root object, useful when the reader uses.asClass()or.asType()showRootTypeInfo()- Explicitly show the@typeon the root object (current default)- Added
isShowingRootTypeInfo()getter toWriteOptions - Validation: These methods are only valid with
showTypeInfoMinimal(). Using them withshowTypeInfoAlways()orshowTypeInfoNever()throwsIllegalStateException— 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
- Control whether
- 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
- Single-line comments:
- 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",}
- Objects:
- 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'}
- Values:
- 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)
- Lowercase:
- 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)
- Leading decimal point:
- 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"
- Backslash followed by LF (
- Added
- FEATURE: JSON5 Write Support -
WriteOptionsBuildernow 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\""}
- Uses single quotes if string contains
- Added
json5InfinityNaN(true)to write Infinity/NaN as literals instead of nullDouble.POSITIVE_INFINITYwrites asInfinityDouble.NEGATIVE_INFINITYwrites as-InfinityDouble.NaNwrites asNaN- 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
- Arrays:
- JSON5 meta key prefixes use
$instead of@for unquoted keys$type,$id,$ref,$items,$keysin JSON5 mode- When combined with
shortMetaKeys(true):$t,$i,$r,$e,$k - All variants are accepted when reading:
@type,@t,$type,$t
- Added
- API:
JsonValue.getReferenceId()now returns primitivelonginstead ofLong- 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 callinggetReferenceId() - Migration: Change
Long ref = obj.getReferenceId(); if (ref != null)toif (obj.isReference()) { long ref = obj.getReferenceId(); ... }
- Previously returned
- API:
JsonValue.setReferenceId()now takes primitivelonginstead ofLong- Previously accepted
Long(null to clear) - Now accepts
long(use 0 to indicate not a reference) - Migration: Change
setReferenceId(null)tosetReferenceId(0)
- Previously accepted
- PERFORMANCE:
JsonWriter- OptimizedtraceReferences()to reduce object allocation- Replaced single
Deque<Object[]>withDeque<Object>plus primitiveint[]for depths - Eliminates
new Object[]{element, depth}allocation for every object pushed during reference tracing - Eliminates
Integerautoboxing by using primitiveint[]with automatic growth - Reduces GC pressure when serializing large object graphs
- Replaced single
- PERFORMANCE:
WriteOptionsBuilder- CacheisNonReferenceableClass()result usingClassValue- 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 inwriteImpl()usingClassValue- Replaced cascade of 6
instanceofchecks with cachedWriteTypeenum lookup - For POJOs (the common case), avoids all
instanceofchecks after first encounter - Uses
switchon cached enum for efficient dispatch to write methods
- Replaced cascade of 6
- PERFORMANCE:
JsonWriter- Write@idand@refvalues directly without String allocation- Changed
writeId()to takelonginstead ofString, eliminatingLong.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
- Changed
- PERFORMANCE:
JsonParser- OptimizedskipWhitespaceRead()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_MAParray - ~4% read improvement in Maps mode, ~1.5% in Java mode
- Replaced array bounds check + lookup (
- 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 cachedJsonTypeenum for type dispatch optimization- Added
getJsonType()method that caches result ofisArray()/isCollection()/isMap()checks Resolver.traverseSpecificType()andJsonWriter.writeImpl()now use cached type dispatch- Avoids repeated type classification checks during object graph traversal
- Added
- 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()andLong.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
- Parse integers directly from
- 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
finalfields at parser construction time
- Hoisted
- 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)
- Removed
- 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
isSystemClassfield andReflectPermissionimport
- PERFORMANCE:
IsMethodAccessorFactory- OptimizedcreateIsName()string creation- Replaced String concatenation with direct char[] construction
- Eliminates intermediate String allocations for boolean accessor method names
- PERFORMANCE:
MethodInjectorFactory- OptimizedcreateSetterName()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
CacheStateinner class - eliminated 1 object allocation per JsonObject - Replaced cached
keySet()copy with lightweightArrayKeySetview class (no data copying) - Replaced cached
values()copy with lightweightArrayValueCollectionview class (no data copying) - Removed unnecessary caching of
keys.lengthanditems.length(already O(1) array access) - Use primitive
byte sortedCacheinstead of boxedBooleanfor sorted state
- Removed
- PERFORMANCE:
Resolver- Early exit for empty post-parse collectionspatchUnresolvedReferences()now returns immediately if no forward references to patchrehashMaps()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
lineandcolfields (8 bytes) - FastReader already removed tracking, these were always 0 - Changed
idfromlongtoint(4 bytes) - 2.1 billion unique IDs is sufficient - Changed
refIdfromLongtointwith -1 sentinel (4+ bytes) - avoids object allocation - Public API unchanged for backward compatibility (
getId()returnslong,getReferenceId()returnsLong) - Error messages now use snippet context instead of useless "line 0, col 0"
- Removed
- CLEANUP:
JsonReader- Removed all implementation code, retaining only deprecated inner interfaces for backward compatibilityJsonReaderclass is now marked@Deprecated- useJsonIofor all JSON parsing- Private constructor prevents instantiation with clear error message
- Deprecated inner interfaces retained for source compatibility:
JsonReader.ClassFactory→ use top-levelClassFactoryJsonReader.MissingFieldHandler→ use top-levelMissingFieldHandlerJsonReader.JsonClassReader→ use top-levelJsonClassReader
- Reduces class from 873 lines to 74 lines
- All functionality now handled by
JsonIo,JsonParser, andResolver - Merged PRs #427 and #428 from @Shiyang-Zhao to correct non-deterministic tests
- DEPENDENCY: Updated
java-utilto version 4.72.0- Includes fix for
ThreadedLRUCacheStrategyscheduled task accumulation - Includes fix for Jackson dependencies incorrectly declared without
<scope>test</scope>
- Includes fix for
- PERFORMANCE:
JsonWriter- Eliminate redundant@typefor Collection and Map elements- When a field is declared with generic type info (e.g.,
List<Person>),@typeis now omitted on elements when the element class exactly matches the declared element type - Extends to Map keys/values when using
@keys/@itemsformat (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
- When a field is declared with generic type info (e.g.,
- PERFORMANCE:
MapResolver- Optimized Maps mode from 9x slower to 4x slower than Jackson- Added
MAP_OPTIONS_CACHEto avoid creatingReadOptionsBuilderon everytoMaps()call - Added
fastPrimitiveCoercion()for common JSON primitive to Java primitive conversions without Converter lookup - Added
isNonReferenceableClasscheck before Converter lookup intraverseArray- user types are not nonRef and Converter cannot convert them - Added early exit for
Object.classor matching types intraverseFields - Cache
readOptionslocal variable intraverseArrayhot loop
- Added
- PERFORMANCE:
Resolver- Added earlyisFinishedcheck inpush()method- Skips pushing objects that are already fully resolved, reducing unnecessary work
- PERFORMANCE: Replaced
Arrayreflection calls with fasterArrayUtilitiesmethods- Uses optimized array operations from java-util for better performance
- FIX:
ArrayFactory- Fixed converting subclass types unnecessarily- Added
isAssignableFromcheck 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.DateinDate[]arrays)
- Added
- ADDED: Aliases for new JDK factory types
AbstractMap.SimpleEntry,AbstractMap.SimpleImmutableEntryReentrantLock,ReentrantReadWriteLockSemaphore,CountDownLatch
- FIX: Added factories and writers for JDK classes with inaccessible
private finalfields on Java 9+:- Java 9+ module system blocks reflection access to
private finalfields injava.basemodule - JDK classes are not compiled with
-parametersflag, so constructor parameter names are synthetic (arg0,arg1), preventing named parameter matching - New Factories (extract state from JsonObject, invoke constructors directly):
SimpleEntryFactory- HandlesAbstractMap.SimpleEntryandAbstractMap.SimpleImmutableEntryReentrantLockFactory- CreatesReentrantLockwith correct fairness settingReentrantReadWriteLockFactory- CreatesReentrantReadWriteLockwith correct fairness settingSemaphoreFactory- CreatesSemaphorewith permits and fairnessCountDownLatchFactory- CreatesCountDownLatchwith initial countOptionalFactory- CreatesOptional.empty()orOptional.of(value)
- New Writers (serialize meaningful state instead of internal
Syncfields):ReentrantLockWriter- Writes fairness settingReentrantReadWriteLockWriter- Writes fairness settingSemaphoreWriter- Writes available permits and fairnessCountDownLatchWriter- Writes current countOptionalWriter- 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
- Java 9+ module system blocks reflection access to
- FIX:
ObjectResolver- Fixed potentialNullPointerExceptionintraverseArray()when array elements are null- Added null check before calling
getClass()on array elements created bycreateInstance()
- Added null check before calling
- 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
maxUnresolvedReferencesandmaxMissingFieldslimits configured inReadOptions - Prevents potential DoS attacks via unbounded memory consumption
- Changed 4 locations from direct collection access (
- FIX:
EnumTests- Fixed flaky test failures caused by cached constructor accessibility- Added
@BeforeEachwithClassUtilities.clearCaches()to ensure test isolation
- Added
- DEPENDENCY: Updated
java-utilto version 4.71.0- Required for
ArrayUtilities.getLength()optimization - FastReader line/column tracking removed for performance (use
getLastSnippet()for error context)
- Required for
- DEPENDENCY: Updated
java-utilto 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 usesStringReaderinstead ofFastByteArrayInputStream, 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)andJsonIo.toMaps(String)calls - Backward Compatible: Zero API changes, all internal optimizations
- Added
- 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
inputandparsertonullwhen not needed for the use case - Impact: Reduced allocation overhead in
JavaObjectBuilderpath (used when converting existing JsonObject graphs to Java objects)
- Optimized
- 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('{')andskipWhitespaceRead()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 setsfailOnUnknownType(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 astoJava()) - 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 setsfailOnUnknownType(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 MapList<Object> list = JsonIo.toMaps("[1,2,3]").asClass(List.class)- Parse JSON array to ListString str = JsonIo.toMaps("\"hello\"").asClass(String.class)- Parse JSON string to StringObject result = JsonIo.toMaps(json).asClass(null)- Parse any valid JSON typeJsonObject 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)vsJsonIo.toJava(json, opts).asClass(Person.class)- intent is clear from method name
- Methods:
- ENHANCED: Updated
JsonIoclass-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
- Java Object Mode:
- DEPRECATED: Marked
toObjects()methods for removal in version 5.0.0:toObjects(String, ReadOptions, Class<T>)→ UsetoJava(String, ReadOptions).asClass(Class)toObjects(InputStream, ReadOptions, Class<T>)→ UsetoJava(InputStream, ReadOptions).asClass(Class)toObjects(JsonObject, ReadOptions, Class<T>)→ UsetoJava(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
ReadOptionsandReadOptionsBuilder:- Removed methods:
getThreadLocalBufferSize(),getLargeThreadLocalBufferSize(),threadLocalBufferSize(),largeThreadLocalBufferSize(),addPermanentThreadLocalBufferSize(),addPermanentLargeThreadLocalBufferSize() - Reason:
JsonParser.readString()reverted to simple state machine using instance fieldStringBuilderinstead of ThreadLocalchar[]buffers - Impact: API simplification - removed 6 public methods and 2 constants that are no longer needed
- Tests removed: Deleted
ThreadLocalBufferIntegrationTest.java(12 tests) and updatedJsonParserSecurityLimitsTest.javato remove ThreadLocal buffer assertions - Migration: No action required - these configuration options had no effect after
readString()revert
- Removed methods:
- IMPROVED:
WriteOptionsBuilderandReadOptionsBuilderalias type name removal methods now useRegexUtilitiesfor 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
- Methods updated:
- CLEANUP: Removed unused import
com.sun.tools.javac.util.StringUtilsfromInjector.javathat was preventing compilation on JDK 17+
- DEPENDENCY: Updated
java-utilto version 4.3.0 for newDataGeneratorInputStreamutility 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)
- Removed redundant
- PERFORMANCE: Optimized
EnumFieldFilterto avoid unnecessaryfield.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
- Moved field name computation after
- FIX: Fixed bug in
ArrayTest.testEmptySubArray()test:- Changed
JsonIo.toObjects()toJsonIo.toJava()for correct API usage - Fixed variable references from
outer[]array toouterList.get()for proper type handling
- Changed
- FIX: Fixed 2 disabled security tests in
JsonReaderSecurityLimitsTest:testObjectReferencesLimit_ShouldUseConfiguredLimit- Added.asClass()to trigger parsing and reference trackingtestReferenceChainDepthLimit_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
MultiKeyMapTestabout nested Set support (already implemented and tested) - Removed 2 misleading TODOs in
TestGraphComparatorandTestGraphComparatorListabout IP address performance (no IP lookup exists, static init is fast)
- Removed outdated TODO in
- 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 - CachedClass<?>at call sites and passed as parameters to methods, avoiding repeatedgetClass()invocations. Fixed two critical paths:writeImpl()→writeArray(): Cache objClass and pass to writeArray() instead of calling array.getClass() againtraceReferences()→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 cacheReferenceTrackeroutside loop, eliminating redundantgetReferences()calls during collection traversal. - PERFORMANCE: Removed redundant injector map caches that duplicated
ReadOptions.getDeepInjectorMap()caching:- Removed
MapResolver.classInjectorCacheinstance field - Was caching results already cached by ReadOptions via ClassValueMap - Removed local
injectorCacheinResolver.patchUnresolvedReferences()- Method-local cache was redundant - Combined with ObjectResolver type cache removal: Overall ~4% improvement in deserialization performance
- Removed
- PERFORMANCE: Removed redundant type resolution cache from
ObjectResolver- Type resolution is already cached byTypeUtilities.resolveType()in java-util, making the additional cache layer unnecessary. Simplifies code and eliminates duplicate string concatenation overhead. - FEATURE: Added convenience read methods to
Resolverfor 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,EnumSetFactorynow use convenience methods - Updated examples:
CustomJsonTest,CustomJsonSubObjectTest,CustomJsonSubObjectsTestdemonstrate usage - Comprehensive documentation: Added "Writing Custom ClassFactory (Reader)" section to user-guide.md with complete examples
- Primitive type methods:
- ENHANCEMENT: Simplified
MultiKeyMapFactorykey 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:
MultiKeyMapFactoryold format handling - Removed support for legacy array-with-marker-strings format that usedinternalizeMarkers()and oldreconstructKey(). All MultiKeyMap serialization now uses the native List/Set format. Requires java-util 4.2.0+ which consolidated to singlereconstructKey()method. - FEATURE: Added comprehensive semantic write API to
WriterContextfor 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 escapingwriteValue(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 serializationwriteNumberField(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 braceswriteStartArray()/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 (useswriteArrayFieldStart()for cleaner code) - Comprehensive Javadoc: All 13 methods include detailed documentation with usage examples and patterns
- Basic field/value methods:
- 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
- Added
- 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)andwriteValue(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 usewriteFieldName()andwriteValue()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 publicgetConfig()API from java-util - Updated
CompactSetWriter: Refactored to use context-awarewriteValue(), 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
- Updated
- 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 testsCharBufferWriterTest: 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.
- FEATURE: Added
useUnsafeoption toReadOptionsto 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.testPrimitivesto accommodate changes in java-util 4.1.0'sClassUtilities.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.CacheStateto implementSerializablefor proper Java serialization support - Fixed
EnumFieldFilterto correctly handle both$VALUES(standard JVM) andENUM$VALUES(Windows JVM) synthetic fields - Fixed tests to handle Windows file path separators (backslash vs forward slash)
- Changed
SecurityTestto use cross-platformjava --versioncommand instead ofipconfig - Fixed
JsonObjectTestserialization test typos - Fixed
EnumFieldFilterTeststo properly validate enum field filtering behavior
- Fixed
- 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
- FIX: Issue #424 - Fixed
maxObjectGraphDepthincorrectly 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(notLinkedHashMap). WhenunknownTypeClassis null and an unknown type is encountered, json-io creates aJsonObjectwhich implementsMap. Users can explicitly setunknownTypeClasstoLinkedHashMap.classor 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.
- Updated java-util from
3.8.0to3.9.0.
- Updated java-util from
3.6.0to3.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
- 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>andMap<? 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
JsonValuetype 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
JsonValuetype 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
JsonWriterprimitive 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()andloadRef()- 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
Resolvercollections - 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
MapResolvercasting 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()andloadSetDefinition()- adds resource path validation to prevent unauthorized file access - SECURITY: Add input validation in
MetaUtilsresource 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
Injectorclass - 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
Accessorclass - 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
JsonParserstring 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
MapResolvermethods with early null exits and cached method lookups - Optimize
JsonReadermethods by caching getClass() calls and using final variables - Optimize
Injector.inject()exception handling with cached field information
- Updated java-util from
3.4.0to3.5.0. - Jar now built with the
-parametersflag enabling constructor parameter name matching - Improved object instantiation logic to use parameter names when available
- Expanded
ThrowableFactoryto support parameter name aliases - Throwable instantiation now delegates to
Converterfor faster construction - Fixed exception message selection when using
ThrowableFactory - Preserve null
causewhen constructing exceptions viaThrowableFactory - Treat empty
causeobjects asnullduring exception instantiation - Ensure message field is written first to preserve constructor argument order
- Minor fixes and test updates
- Reflection usage in
ReadOptionsBuilderandInjectornow leveragesReflectionUtilscaching - Optimized
JsonObject.get()method with binary search for sorted String keys (>8 elements) - Cache
keySet()andvalues()collections inJsonObjectto avoid repeated creation - Optimize
containsKey()andcontainsValue()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
JsonObjectto eliminate O(n) scans on everyget()operation for large arrays - Cache array lengths in
JsonObjectto eliminate expensiveArray.getLength()JNI calls
- Updated java-util from
3.3.2to3.4.0. - Added class-level Javadoc for
ByteArrayWriterdescribing Base64 encoding - Fixed deserialization of Java records
- Tests now create record classes via reflection for JDK 8 compile compatibility.
- InjectorPrivateConstructorsTest now uses
ReflectionUtilsfor reflective calls. - Replaced
System.out.printlndebug output with Java logging viaLoggingConfig - SealableNavigableMap now wraps returned entries to enforce immutability
- Preserve comparator when constructing
SealableNavigableSetfrom aSortedSet - Documentation expanded for CompactMap usage and builder() caveats
- JsonObject exposes
getTypeString()with the raw@typevalue - 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
retainAllresult inSealableNavigableSetAdditionalTest - 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
SingletonListtests 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
- Updated java-util from
3.3.1to3.3.2.
- Updated java-util from
3.3.0to3.3.1. - Removed unused ClassFactories.
- Added
ReadOptions/WriteOptionsability to specifynotCustmoRead/Writtenclasses innotCustomRead.txtandnotCustomWritten.txt
CompactMapandCompactSetJSON formats improved - match historical formats, except when using builder pattern.ByteBuffer and CharBufferconverstions to/fromMapadded.- Performance improvements in JSON serialization and deserialization.
- Code simplification related to instance creation for common types.
- Updated java-util from
3.2.0to3.3.0.
- JSON Serialization Improvements
- Implemented specialized JSON serialization for
CompactMapandCompactSetwith 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
- Implemented specialized JSON serialization for
- Collection API Consistency
- Aligned
CompactSetAPI withCompactMapfor better usability
- Aligned
- Improved JsonObject Implementation
- Consistent
MapInterface: EnhancedJsonObjectto implement theMapinterface more consistently: - Fixed the internal storage mechanism to properly handle different storage states
- Ensured
Mapmethods work correctly regardless of internal storage approach
- Consistent
- 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.1to3.2.0.
- Enhanced Type System Support
json-ionow leveragesjava-util's sophisticatedTypeUtilitiesframework to provide comprehensive resolution of complex Java generics:- Full support for
ParameterizedTypes(e.g.,Map<String, List<Person>>) - Proper handling of
GenericArrayTypesfor arrays with generic components - Resolution of
TypeVariablesin 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.
- Full support for
- Performance improvements in parsing and resolving.
- New Type-Aware API Capabilities
- Added advanced type specification via the new
TypeHolderclass:- 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
JsonIoclass now acceptsTypeHolderinstances, allowing precise type targeting beyond what's possible with rawClassreferences alone.
- Added advanced type specification via the new
- 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 typesasType(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
- Introduced a new fluent builder pattern through the
- Updated java-util from
3.0.3to3.1.0.
- All Time and Date classes compressed to a single String representation in JSON, uses ISO formats when possible.
- Java's
PatternandCurrencysupport added - Updated java-util from
3.0.2to3.0.3.
- New custom
ClassFactoryclasses are easier to write:- See examples
ByteBufferandCharBuffernow natively supported.CompactMapsupported added viaCompactMapFactoryandCompactMapWriter.Sealable*tests moved fromjava-utiltojson-io(and so have theSealable*classes).
- 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
@keysand@itemsnow as explicit fields. - JsonObject simplified, enumType has been removed, as it is now stored in JavaType field.
- Root object types like
Person[].classorString[].classsupported for casting, as opposed to onlyObject[].class.
- scrub release.
- Root object type's like
Person[].class,String[].class,can now be specified as therootTypeand the return value will bePerson[],String[],or aClassCastExceptionif 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 JSONStringdirectly.
- Consumed
java-util'sClassUtilities.getClassLoader(),which obtains the classLoader in a more robust way and works in OSGi and JPMS environment or non-framework environment - Removed
slf4jandlogback-classicfromtestdependencies - Merged in PR #297 by DaniellaHubble: Fix test that fails unexpectedly in
testEnumWithPrivateMembersAsField_withPrivatesOn() - Updated java-util from
2.15.0to2.17.0.
- Updated java-util from
2.14.0to2.15.0.
ReadOptionsBuilder.addInjectorFactory()added to allow additionalInjectorFactory'sto be added.ReadOptionsBuilder.addFieldFilter()added to allow additionalFieldFiltersto be added.- LRU size control added to
ReadOptionsBuildandWriteOptionsBuilder. These control the LRU size of the cache that mapsClassestoFields,ClassestoInjectors, andClassestoAccessors. - Adds
bigint,BigInt,bigdec,BigDec,String,Date,andClassto aliases to java-util'sClassUtilities.forName()support - Updated java-util from
2.13.0to2.14.0.
- Performance improvement for
JsonIo: When usingnullfor defaultReadOptionsorWriteOptions,the same static instance is used. - Updated java-util from
2.10.0to2.13.0.
JsonParsernow 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 = ZoneRulesis now correctly specified (it hadjava.time.ZoneRulesbefore). - When
nullpassed in forReadOptionsorWriteOptionstoJsonIoAPIs, an already created default instance ofReadOptionsorWriteOptionsis returned to improve performance (no need to reconstruct the default instance). - Updated java-util from
2.9.0to2.10.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 onReadOptionsBuilderandWriteOptionsBuilder.If you do not want a particular alias output, useWriteOptionsBuilder.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 toObjectto allow custom writers to write@id, @refif desired.
Collections.unmodifiableXXX()instances when serialized, restore back to unmodifiable instances.ImmutableListandImmutableSetrestore back unmodifiable instances.ReadOptionsBuildernow include all extended aliases by default (.withExtendedAliases()). You can take advantage of this on the sending side by using theWriteOptionsBuilder().withExtendAliases().We will default this on theWriteOptionsBuilderin 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.
- Many more
@typealiases added to keep the JSON succinct and more human-readable. - Broader conversion support for rootTypes:
JsonIo.toObjects(..., rootType)Includes all thejava-utilsConverter.convert()pairings (680+) - Removed
stackargument from CustomReader. When creating a CustomReader, use the passed inresolver.push(node)to push objects onto the stack for later processing (custom or not). See example in UserGuide (coming shortly).
- 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().ThewriteOptionsare now created with defaults for you if null is passed in. - Added
Resolveras the last argument to theJsonClassReader.read()method. The author is not required to use theResolverin their implementation, but it does it come in handy as it has the Map of IDs to JsonObjects, as well as theReadOptions,and theConverter. - Deprecated the APIs on
JsonIothat exist to show one how to convert the old styleMapoptions to the new "builder" format.
MethodFiltercan 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'sare added toWriteOptionsvia theWriteOptionsBuilder.MethodFilter'scan be added to a singleWriteOptionsinstance or added permanently (jvm lifecyle) so that all createdWriteOptionsinclude it automatically (seeWriteOptionsBuilder.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.
ReadOptionsBuilderdid not have thewithExtendedAliases()option. This adds in all theconfig/extendedAliases.txtaliases, dramatically shrinking the size of common Java class names in the JSON@typefield.- Both
ReadOptionsBuilderandWriteOptionsBuildercan take an existingReadOptionsorWriteOptionsas a starting point, allowing you to copy from an exist options, and then tweak it from there.
- Added
JsonIo.getReadOptionsBuilder(Map options)andJsonIo.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 atcom.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.6to2.4.8.
- Removed references to JsonObject from transition classes com.cedarsoftware.util.io.JsonReader.
- 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
WriteOptionsandReadOptions.
- NOTE: Repackaged resources into config/resources
- 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.
- Handle NoSuchMethodError() quietly, for shaded accessFactories that were added by containing platform, that no logner exist.
- 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.
- Updated
JsonReaderfor backwards compatibility by addingjsonToJava(), jsonToMaps(), jsonObjectsToJava()static APIs.
- In
ReadOptionsBuilderandWriteOptionsBuilder, when loading dynamic items (class names, aliases, etc.) from resources, output warnings as opposed to throwing exceptions.
- Remove
ReflectionUtilsfrom json-io as it is part of java-util. - Updated java-util from
2.4.4to2.4.5.
- Moved more settings/properties from source code to resource files.
Example changes required due to this update:
BeforeA. Employee e = (Employee) JsonReader.jsonObjectsToJava(JsonObject employee, readOptions)After
A. Employee e = JsonIo.toObjects(JsonObject, readOptions, Employee.class)
- The old
Mapoptions method has been superceded by passing instead aWriteOptionsorReadOptionsinstance. All the prior features are still supported, plus new features have been added. Use the methods onWriteOptionsBuilderandReadOptionsBuilderto 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)
- User Guide documentation on how to specify "options" to
JsonReader/JsonWriterthe new, easier way. The old Map options method has been superceded by theWriteOptionsandReadOptionsapproach. All the prior options are still supported, plus new features have been added.
- 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.
- 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.
JsonReader/JsonWriterOptions are now specified usingReadOptionsBuilder.build()andWriteOptionsBuilder.build().- For improved security, key JDK classes like
ClassLoader,Process(and derived classes),Method,Field,Constructorand 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
JsonReaderandJsonWriterhas been simplified to fewer options and many of the prior APIs have been deprecated. The 5.0.0+ release will remove these deprecated APIs.
- Supports
JDK1.8, JDK11, 17, 21.Tested with these versions, and compiled in class file version 52 (JDK1.8) format. ClassFactoryaddedisFinalObject() { return true/false }to prevent additional processing from happening if theClassFactorycreates 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
Enumsin a more compact way with the field name associated to a JSON String name of the enum. However, the prior versions ofjson-iowroteEnumsout as JSON objects. The JSON reader will readEnumseither way. If you want the output to continue to writeEnumsas a JSON Object, use the.writeEnumsAsObjects()on theWriteOptionsBuilder, and it will output enums as it used to. - Minor change:
JsonObjectwasJsonObject<K, V>and is nowJsonObject(no generics). If you usedJsonObjectin your code, make sure to remove the generics. - Minor change:
JsonReader.ClassFactory::newInstance(Class c, Object)has been changed toJsonReader.ClassFactory::newInstance(Class<?>, JsonObject). If you have written aCustomClassFactory,update the method signature tonewInstance(Class<?>, JsonObject).
Enum/EnumSetsupport fully added @kpartlowWARNThis version inadvertently slipped toJDK11+(which has been corrected in4.15.0). Version5.x.xwill beJDK11 or JDK17.
- JDK 1.8 is target class file format. @laurgarn
- JDK 11 is source file format. @laurgarn
- Bug fix:
EnumSetsupport 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
- Bug fix:
Enumserialization 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
EnumSetwith json without type #120. Fix by @sgandon and @wwang-talend
- Enhancement: Clear unresolved references after all have been processed, as opposed to removing each one after it was processed.
- Bug fix: Enhancement #137 introduced bug for negative numbers on simple values when tolerant/lenient parsing of +/- infinity was turned on.
- Enhancement (#140): New option flag added
FORCE_MAP_FORMAT_ARRAY_KEYS_ITEMS:true|falseto allow forcing JSON output format to always writeMapas@keys/@itemsin the JSON (example:{"@keys":["a", "b"], "@values":[1, 2]}, rather than its default behavior of recognizing allStringkeys and writing theMapas a JSON object, example:{"a":1, "b":2}.The default value for this flag isfalse.
- Enhancement (#137): Allow tolerant/lenient parser of +/- infinity and NaN. New API added,
JsonReader.setAllowNanAndInfinity(boolean)andJsonWriter.setAllowNanAndInfinity(boolean). The default isfalseto match the JSON standard. - Enhancement (#129):
JsonReader.jsonToJava("")orJsonReader.jsonToJava(null)now returns anull, rather than throwing an exception. - Bug fix (#123): Removed vulnerability by disallowing
ProcessBuilderto 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'sFieldclass. 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.
- Enhancement: Made
FastPushbackBufferedReaderconstructor public so that this stream reader can be used anywhere.
- Bug fix: When reading into
Maps, logical primitives that are notlong,double,boolean, ornull, were being kept inJsonObjectsinstead of being converted into their respective types (int,float,Date, etc.)
- Bug fix: Line number was incorrectly being reported as column number in error output.
- Enhancement: Added nice JSON-style argument format method, typically used for logging method calls. See
MetaUtils.getLogMessage().
- 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
- Enhancement: Missing field handler improvements. Submitted by @sgandon
- Enhancement: Missing field handler improvements. Submitted by @sgandon
- Enhancement: Added
JsonReader.addReaderPermanent()andJsonWriter.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 ofJsonReader/JsonWriteror permanently, so they do not have to be added each instantiation (through args or call.addReader()or.addWriter()).
- Enhancement: Improved
enumhandling. Updated how enums are detected so that subclasses of enums are detected.ordinalandinternalfields no longer output.
- 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.
- 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.
- Enhancement: Double.INF and NAN are output as null.
- 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.
- Enhancement: Make it possible to assign instantiator for package private classes, for example com.google.common.collect.RegularImmutableMap. Contributed by @mhmx (Richard Kovacs)
- Enhancement: AtomicInteger, AtomicLong, and AtomicBoolean are now supported.
- 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.
- 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.
- Bug fix: custom write serializers were being cleared in the
write()method, not theclose()method after full serialization completed. @darmbrust - Enhancement: Access increased to public for the pretty-print support apis,
tabIn(),tabOut(), andnewLine(). @darmbrust
- 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
JsonReader.jsonToMaps()API is no longer recommended (not yet deprecated). These can easily be turned intoJsonReader.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).
- Enhancement: Skip null fields. When this flag is set on the
JsonWriteroptional arguments, fields which have a null value are not written in the JSON output.
- 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.
- 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).
- 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.
- 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.
- Bug fix: When writing a Map that has all String keys, the keys were not being escaped for quotes (UTF-8 characters in general).
- 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.
- 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.
- Better support for primitive output when 'never show type' is set. (submitted by @KaiHufenbach)
- 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)
- Bug fix: Custom readers will now always have the .target field set if a
JsonObjectis passed to them. The custom reader'sread()method was being called before the.targetfield was set on theJsonObject.
- Made
JsonReader / JsonWriter getObjectsReferenced()APIpublic(allows custom reader / writers access to these) Resolver.createJavaObjectInstance(), used to create the correct Java object for aJsonObjectpeer, no longer calls the .read() API for objects's with custom readers.
- 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.
- JDK 1.6 support - Use of
ReflectiveOperationExceptionchanged toInvocationTargetException.
- JDK 1.6 support restored. Keeping 1.6 support for Android developers. Submitted by @kkalisz
- 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.
- 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()andJsonReader.jsonToMaps()now allow anInputStreamto 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(), andremoveNotCustomWriter()APIs have been removed since customizers are set per-instance.
- Added new
JsonObject.isReference()API which will return 'true' if theJsonObjectis currently representing a reference@ref - Added new
JsonReader.getRefTarget(jsonObject)API which will follow the@reflinks until it resolves to the referenced (target) instance. - Added new
JsonReader()constructor that only takes the args (Map). It is expected that you will callJsonReader.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 anot customreader - if anotCustom()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 anot customwriter - if anotCustom()writer has been added (preventing inherited object from using custom writer), the association can be eliminated using this API.
- 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.
- Consolidate all 3.2.x changes
- Last snippet read no longer shows 'boxes' for unused internal buffer characters.
JsonWriter- moved reference check 'up' towriteImpl()so that each specific 'write' routine did not have to test / callwriteOptionalReference().- If you have a custom reader that does not bother to resolve references from 'deeper' internal
JsonObjectmaps, 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.
- Cache Map's for custom reader's updated to be
ConcurrentMapinstead ofMap.
JsonCustomReaderExadded, which passes the 'args'Mapthrough to the custom reader.- Both
JsonCustomReaderExandJsonCustomWriterExhave aMapas the last argument in their single method that is implemented by the custom reader / writer. ThisMapis the same as the 'args'Mapassed into to theJsonReader/JsonWriter, with the addedJSON_READERorJSON_WRITERkey and associated value of the callingJsonReader/JsonWriterinstance.
- Made
Support.getWriter()methodpublic staticso that CustomWriters can easily use it - Changed
JsonCustomWriterExto no longer inherit fromJsonCustomWriterand instead added a common parent (JsonCustomWriterBase). This allows only one method to be overridden to create aJsonCustomWriterEx.
- New
JsonCustomWriterExinterface which adds theJsonWriteraccess to the implementing class so that it can call back and usejsonWriter.writeImpl()API. - Change
JsonWriter.writeImpl()from protected to public
- Performance improvement: No longer using .classForName() inside JsonObject to determine isMap() or isCollection(). Reading JSON into Map of Maps mode significantly faster.
- 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.
JsonReader.UNKNOWN_OBJECTadded as an option to indicate what to do when an unknown object is encountered in the JSON. Default is aMapwill be created. However, you can set this argument to aStringclass name to instantiate, or set it to false to force an exception to be thrown.
*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.
- Bug fix: Using a CustomReader in a Collection with at least two identical elements causes an exception (submitted by @KaiHufenbach).
- Added new flag
JsonWriter.WRITE_LONGS_AS_STRINGSwhich 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 754doublefor 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.)
- Performance improvement: caching the custom readers and writes associated to given classes.
- Ease of use:
json-iothrows aJsonIoException(unchecked) instead of checked exceptionIOException. This allows more flexibility in terms of error handling for the user. - Code cleanup: Moved reflection related code from
JsonReaderinto separateMetaUtilsclass. - Code cleanup: Moved
FastPushbackReaderfromJsonReaderinto separate class. - Code cleanup: Moved JSON parsing code from
JsonReaderinto separateJsonParserclass. - Code cleanup: Moved built-in readers from
JsonReaderto separateReadersclass. - Code cleanup: Moved resolver code (marshals map of maps to Java instances) into separate
Resolverclasses.
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).
- Bug fix: When writing a
Mapwith JSON primitive keys (String,Long,Double, orBoolean), aClassCastExceptionwas being thrown if the type wasLong,Double, orBoolean. This has been fixed with test added.
- Android: Rearranged
[:.]to[.:]in regular expressions for Android compatibility. Technically, it should not matter, but[:.]was causingjava.util.regex.PatternSyntaxException: Syntax error U_ILLEGAL_ARGUMENT_ERRORon Android JVM. - Bug fix: When using the
JsonWriterargumentsMapwithFIELD_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, theMapcan contain any non-static fields in the class, including transient fields. - All JUnit tests converted to Groovy.
- Bug fix: Parameterized types are only internally stamped onto generic Maps (Maps read with no
@type) if the field that points to theMapis a template variable or it has template arguments. - Performance optimization: tracing references specially handles
CollectionandMap. By avoiding internal structures, the reference trace is much faster.
- Unmodifiable
CollectionsandMapscan now be serialized. - Added tests to ensure that
JsonReader.jsonToMaps()coerces the RHS values when logical primitives, to the optional associated@type'sfields. - More tests and improved code-coverage.
- Bug fix:
JsonReader.jsonToMaps()API was incorrectly attempting to instantiate peer objects (specified by "@type" field in the JSON) when in 'maps' mode. This madeJsonReader.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.
- Additional attempt to instantiate classes via
sun.misc.Unsafeadded (optional must be turned on by callingJsonReader.setUseUnsafe(true)). json-io already tries all constructors (private or public) with varying arguments, etc. If this fails and unsafe is true, it will trysun.misc.Unsafe.allocateInstance()which effectively does a C-stylemalloc(). This is OK, because the rest ofJsonReaderfills in the member variables from the serialized content. (Submitted by @KaiHufenbach).
- Performance optimizations. Use of switch statement instead of if-else chains.
- JDK 1.7 for source code and target JVM.
- Bug fix: ArrayIndexOutOfBounds could still occur when serializing a class with multiple Templated fields. The exception has been fixed.
- 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.
JsonReaderexecutes 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 forDatenow parses, full time zone support, extra whitespace allowed within the date string. - Added ability to have custom JSON writers for interfaces (submitted by @KaiHufenbach).
- When writing JSON, less memory is used to manage referenced objects.
JsonWriterrequires 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.
BigDecimalandBigIntegerare now always written as a primitive (immutable, non-referenced) value. This uniformizes their output.
- Updated to support JSON root of
String,Integer, Floating point, andBoolean, per the updated JSON RFP. Example, theString"football" is considered valid JSON. TheJsonReader.readObject()API andJsonReader.jsonToJava()will return aStringin this case. TheJsonReader.jsonToMaps()API will still return aMap (JsonObject), and the@itemskey will contain anObject[]with the single value (String, Integer, Double, Boolean) in it. - When a Java
Maphas onlyStringkeys in it, json-io will use the JSON object keys directly and associate the values to the keys as expected. For example, theMap['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@keysis an array [] of all the keys, and the@itemsis an array [] of all the values. Entry 0 of@keysmatches with Entry 0 in the@itemsarray, and so on. Thanks for Christian Reuschling for making the request and then supplying the implementation. - Change some APIs from
privatetoprotectedto allow for subclasses to more easily override the default behavior.
- Bug fix: An internal
Mapthat kept meta-information about a Java Class, changed toConcurrentHashMapfromHashMap.
- Added support for specifying which fields on a class will be serialized. Use the
JsonWriter.FIELD_SPECIFIERSkey and assign the value to aMap<Class, List<String>>, where the keys of theMapare classes (e.g. Bingo.class) and the values areList<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 aClassLoaderreference as a non-static, non-transient field. You may not have access to the source code to mark the field astransient. In this case, add the appropriate entries in theFIELD_SPECIFIERSmap. Voila, problem solved. Use theJsonWriterAPI that takesoptionalArgs Map. The key for thisMapisJsonWriter.FIELD_SPECIFIERand the value isMap<Class, List<String>>.
java.net.URLcan now be used as a constructor argument. The reader was throwing an exception instantiating a constructor with aURLparameter.java.lang.Objectparameters in constructor arguments are now tried with both null andnew Object()now.
- Fixed a bug (introduced in 2.5.0) in the processing of a
Mapthat has aCollectionas a key.
- New 'Pretty-Print' option available. If the 'args' Map passed to
JsonWriter.objectToJson(o, args)contains the keyJsonWriter.PRETTY_PRINTand the value 'true' (booleanorString), theJsonWriteroutput will be formatted in a nice human readable format. - Convert a JSON String to Pretty-Print format using
JsonWriter.formatJson(String json). AStringwill 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),JsonReaderwill use those fields to process objects deep within Maps, Collections, etc. and still create the proper Java class.
- Allow "" to be set into
Datefield, setting theDatefield (orDatearray element) as null.
- Allow "" to be set into
BigIntegerorBigDecimalwhen return value isMap(JsonObject). "" to non-String fields will be null, except for primitives and primitive wrappers, that will result in JVM default value.
- 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.
- Added support to allow primitives and
Stringto 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.
- Primitives can be set from
Strings Stringscan be set from primitivesBigDecimalandBigIntegercan be set from primitives,Strings,BigDecimal, orBigInteger
MapsandCollections(Lists,Set, etc.) can be read in, even when there are no@keysor@itemsas would come from a Javascript client. *json-io will now use the generic info on aMap<Foo, Bar>orCollection<Foo>object's field when the@typeinformation is not included.json-io will then know to createFooinstances,Barinstances, etc. within theCollectionorMap.- 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
@typeto be written (not recommended) by putting theJsonWriter.TYPEkey in theJsonWriterargs map, and assigning the associated value totrue.
- Date/Time format can be customized when writing JSON output. New optional
Map argsparameter added to main API ofJsonWriterthat specifies additional parameters forJsonWriter. Set the key toJsonWriter.DATE_FORMATand the value to aSimpleDateFormatstring. Two ISO formats are available for convenience as constants onJsonWriter,JsonWriter.ISO_DATE_FORMATandJsonWriter.ISO_DATE_TIME_FORMAT. JsonReaderupdated to read many different date/time formats.- When
JsonReaderencounters a class that cannot be constructed, you can associate aClassFactoryto 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)
- 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.)
java.sql.Datewhen read in, was instantiated as ajava.util.Date. This has been corrected.
- First official release through Maven Central