3.0.9- 2026-11-03
Breaking changes
- With Ethlo present,
date-timenow rejects space-separated values such as2020-01-01 12:00:00Zand leap-second strings without an offset such as2016-12-31T23:59:60. Ethlo previously accepted these forms. RFC 3339 requiresT/tand an explicitZ/zor signedHH:MMoffset; callers relying on those spellings must supply the required separator/offset. The fallback already rejected both forms. (#1316)
Added
- Pin public JSON/YAML mapper tree-deserializer delegation with regression tests and document the required checks for Jackson upgrades. #1363
- Opt-in
ExecutionConfig.maxEvaluationStepsandmaxEvaluationDepthlimits for validation and schema walking, including walking without assertions. Exhaustion throwsValidationLimitExceededExceptioninstead of returning a partial validity result. Both limits default to unlimited; they bound admitted evaluation steps and schema frames, not work inside a keyword. (#1276, #1289)
Changed
- Decode recognized YAML nonfinite float tokens with Jackson 3.1.8 during tree reads, including explicitly tagged case/sign variants and decimal-mode readers. Preserve the configured nonfinite-to-BigDecimal coercion policy. #1228
- Render null enum members as
nullin validation errors and avoid listing null twice for nullable schemas. #1373 - Avoid duplicate numeric text-buffer reads when classifying JSON/YAML decimals; pin fast-path boundaries, randomized classification, and Jackson 3 name-matcher cache invalidation with regression tests. #1368
- Reduce decimal-preserving tree parser overhead by caching per token and avoiding text and decimal allocations for lossless floats. #1362
JsonMapperFactory.getInstance()andYamlMapperFactory.getInstance()now returnDecimalNodevalues for lossy finite floating-point spellings when reading trees, subject to explicit mapper settings and custom node factories. (#1357)- Preserve lossy finite decimal literals uniformly in built-in JSON/YAML schema and instance readers and public mapper-factory tree reads. Bounds and
multipleOfnow validate decimal spellings such as9007199254740993.0exactly. Decimal nodes can surface in literals, defaults, examples, annotations and JSON output; finite numeric equality no longer depends on the field name. Lossless doubles and signed zero retain type/sign/serialization. Existing overflow/nonfinite behavior, count-specific precision, explicit mapper settings, custom node factories and scalar/POJO binding policies are retained. This intentionally changes validation where values previously rounded to the same double. (#1357) - Reject space separators in the
date-timeformat when format assertions are enabled, while retaining uppercaseTand lowercasetseparators. (#1316, #1331) Thanks @curforever - Accept RFC 3339
date-timespellings with-00:00, offsets through ±23:59 and fractions longer than nine digits when Ethlo is present. Share grammar/range checks while preserving Ethlo's historical leap-second table and the fallback's possible-leap-second policy; inherited historical-table differences remain. (#1316) - With
typeLooseenabled, numericenummembers now match strings by exact decimal value on both master and 2.x:enum: [100]accepts"100","100.0"and"1E+2". Previously master required normalized exponent text while 2.x used plain decimal text. This intentionally broadens accepted numeric spellings. Inputs must follow JSON number syntax; whitespace, leading+, leading integer zeros and numeric separators are rejected. Signed zero and equivalent trailing-zero/exponent forms compare equally. Exponents are scanned with bounded arithmetic without expanding zeros or constructing a large exponent integer. Strict mode, literal string members, container handling, non-finite member text and protected enum conversion hooks retain their existing behavior. The comparison uses the enum node's existing decimal value; precision already lost by schema parsing is not recovered. (#1349) - Share numeric value equality across
const,enumanduniqueItems, including numbers nested in arrays and objects. Numeric hashing handles extreme decimal scales without overflowing during trailing-zero normalization; non-finite float and double nodes compare consistently. Preserveenumconversion hooks; skip container members during loose string matching and prepare scalar matching values once. (#1307) - Preserve count and length limits beyond the
intrange forminItems,maxItems,minProperties,maxProperties,minLength,maxLength,minContains, andmaxContains. Count-limit error arguments are ungrouped Strings; plain integer limits keep their full decimal spelling, while exponent-form decimals remain bounded in size. Contains annotations use the parsed node's own numeric value and type without int-range coercion. Accepted YAML numeric separators retain their meaning. (#1334, #1346) - Store lossy count values in ordinary DecimalNodes so copies and moved/rebuilt schema trees retain precision without a global side table. Contains annotations expose the node's Number value/type. Integral floating/decimal count messages use plain digits through 100 integer digits and bounded scientific form beyond that; integer nodes keep full spelling. Count tokens outside BigDecimal's parsing range retain ordinary Infinity/underflow behavior. The original count-name-only finite precision policy is generalized in #1357. (#1350)
- Without Ethlo on the classpath, the
date-timeformat now checks the date with the rules of thedateformat and the time with the rules of thetimeformat instead ofOffsetDateTime.parse(..). It rejects a time without seconds (2020-01-01T12:00Z), an offset without minutes (+01) and an empty fraction (12:00:00.Z), which Ethlo already rejected, as well as an offset with seconds (+01:00:00). It accepts an offset up to23:59and a fraction longer than nine digits, which RFC 3339 allows, and a leap second at the end of a June 30 or a December 31 in UTC, which it used to reject. Compared with Ethlo, the fallback provides less accurate leap-second validation because it does not check that a leap second was actually inserted on that date. The subsequent shared grammar changes in #1355 also enable these offset/fraction forms with Ethlo and reject space separators on both paths. (#1316, #1337, #1335, #1338) Thanks @Obrams - The
timeformat now rejects a leap second with an hour or minute out of range (24:59:60+01:00,23:89:60+00:30,45:59:60+22:00), which it used to accept. A time without seconds (12:00Z) was already rejected. (#1316, #1337) - Ignore zero
multipleOfdivisors returned by custom validators, regardless of scale, matching the default conversion. (#1328, #1329) Thanks @PHJ2000 - Display
multipleOfdivisors in error messages using plain notation when it fits within 32 characters or is no longer thanBigDecimal.toString(), including values returned by custom validators. Check the length before expanding to plain notation to avoid excessive padding for extreme scales. (#1328, #1329) Thanks @PHJ2000 MultipleOfValidator.getDivisorandgetDividendnow preserve exact integral and BigDecimal values. Custom validators, including overrides callingsuper, receive these exact values; validation uses hook results without further rounding. Integral dividend scales can change (for example,2.0becomes2), which affects scale-sensitive comparisons in overrides. Mixed integral/Double or Float inputs above 2^53 can produce different results. Precision already lost when parsing floating-point values is tracked separately in #1293. (#1286, #1290) Thanks @PHJ2000- Extend document-root unknown-keyword suppression to external
$refJSON Pointer targets. Keep container and direct-schema caches separate so direct registry loads retain normal diagnostics regardless of registry load order, including rejection byDisallowUnknownKeywordFactory. Within one schema, a whole-document reference following a JSON Pointer reference can still reuse the container and suppress root diagnostics, including strict-factory rejection. Apply the container annotation fallback after nullable/discriminator special handling and make the container marker volatile. (#1325, #1326) - Treat unknown document-root fields as annotations when loading a schema by JSON Pointer fragment, while preserving known keyword processing and diagnostics in subschemas. With
DisallowUnknownKeywordFactory, such fragment loads now succeed instead of throwing on unknown root fields; direct document loads still throw. If the selected schema references the root with$ref: "#", unknown root fields also remain annotations and do not reach the unknown-keyword factory. (#1205, #1323) - Unlimited validation/walking bypasses execution counters and keeps the original recursion frame structure. During an active limited execution, reentrant convenience calls share the budget and the caller's evaluation path; unlimited calls retain their legacy root-path initialization.
- Exhausted contexts reject public errors/annotation access. Walk listeners can inspect
ExecutionContext.isEvaluationAborted()andgetEvaluationAbort()during cleanup; an empty error slice after abort is not a validity verdict. Subclass builders supporting limits must use the protected builder constructor or forward both limits explicitly, since the legacy seven-argument constructor remains unlimited. Error.getMessageKey()now returns the message key when the schema supplies a custom message, such astypeorrequired, instead of the custom message text. Unresolved$ref,$dynamicRefand$recursiveReferrors returninternal.unresolvedRefinstead ofReference {0} cannot be resolved. UsegetMessage()for the text andisCustomMessage()to detect custom messages. Errors created without a key still use their message pattern as the key. (#1319, #1320)- Validate fractional seconds and reject trailing text in the
timeformat. (#1308, #1314) Thanks @MoChiUaena - Ignore
minContainsandmaxContainsbefore Draft 2019-09, and treatmaxContains < minContainsas a validation failure rather than a schema error. (#1303, #1295) Thanks @ItsDeadlyProgrammer - Correct
uniqueItemsvalidation for non-array inputs and compare items without copying them. (#1297, #1305) Thanks @PHJ2000 and @ItsDeadlyProgrammer - Add an error reference document and document recursive applicator costs and invalid
multipleOfdivisors and bounds. (#1317, #1287, #1332, #1358) - Ensure the Issue792 and space-separator regression tests run. (#1371) Thanks @sharanggupta
- Update Ethlo from 1.14.0 to 1.17.0 for date-time offset validation. (#1313) Thanks @MoChiUaena
- Change Jackson from 3.2.1 to 3.1.8, following intermediate upgrades to 3.2.3 and 3.2.4. (
231c34b,377877b,558cc18) - Upgrade SLF4J from 2.0.19 to 2.0.20, test dependencies Logback from 1.6.3 to 1.6.6 and JUnit from 6.0.1 to 6.1.3, Maven GPG from 3.2.7 to 3.2.8, and Central Publishing from 0.11.0 to 0.12.0. (
95825f3,83905ab,16f3495,f2d22d1,5515315,276fd58)