Skip to content

feat: record based validation schemas#86

Merged
jbee merged 32 commits into
mainfrom
json-validation-classes
Mar 12, 2026
Merged

feat: record based validation schemas#86
jbee merged 32 commits into
mainfrom
json-validation-classes

Conversation

@jbee

@jbee jbee commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

TLDR;

  • adds to(Class) API (ah-hoc access based mapping to Java types)
  • adds record-type based validation
  • lots of performance improvements to reduce memory footprint by avoiding allocations and reduce CPU load by making operations do less expensive stuff

to-conversion

TBD

API Changes

Changes from upgrading the access concept, mainly introduction of the to(Class) method and support for mapping to records and using records as validation schemas.

  • ✨ relaxed ? extends JsonObject restriction on all Class arguments for a validation schema
  • 🆎 replaced JsonTypedAccessStore with JsonAccessors
  • 🆎 replaced JsonValue#getAccessStore with JsonValue#getAccessors
  • 🆎 replaced JsonGenericTypedAccessor with JsonAccessor
  • 🆎 replaced JsonTypedAccessor with SimpleJsonAccessor
  • 🆔 renamed JsonTypedAccess to JsonAccess
  • ❌ removed JsonValue#isAccessCached
  • ❌ removed JsonValue#withAccessCached
  • 🆕 added JsonAccessException
  • ❌ removed METHOD as @Target for @Validation, @Validator and @ValidatorRepeat (covered by TYPE_USE)
  • 🆎 replaced METHOD as @Target for @Required with TYPE and TYPE_USE (covers METHOD)

Changes from upgrading to use Text views instead of String to represent path segments.

  • ✨ relaxed most methods in JsonObject and JsonNode API accepting a member name as String now accept a CharSequence
  • ✨ relaxed JsonNode#isElement to be defined on arrays and objects
  • JsonPath#toString now throws a JsonPathException when the path cannot be represented with syntax
  • ❌ removed JsonMixed, JsonValue and JsonNode of(Reader) method (using Text is a better way now)
  • ❌ removed JsonPath#keyOf (no longer needed)
  • ❌ removed JsonPath#shortenedBy (no longer needed)
  • ❌ removed JsonPath#dropFirstSegment (no longer needed)
  • ❌ removed JsonPath#objectMemberAtStart (no longer needed)
  • ❌ removed JsonPath#arrayIndexAtStart (no longer needed)
  • ❌ removed JsonPath#startsWithObject (no longer needed)
  • ❌ removed JsonPath#startsWithArray (no longer needed)
  • 🆔 renamed JsonPath#ROOT to SELF (as it not only root but mostly a self reference)
  • 🆔 renamed JsonPath#size to length (more fitting as it is not a collection)
  • 🆔 renamed JsonPath#dropLastSegment to parentPath
  • 🆔 renamed JsonPath#extendedWith to chain (single element) and concat (potentially multiple) for clarity
  • 🆕 added JsonPath#extendedWith(Text)
  • ❌ removed JsonNode#memberOrNull(String)
  • ❌ removed JsonNode#member(String)
  • 🆕 JsonNode#get(Text)
  • 🆕 JsonNode#getOrNull(Text)
  • 🆕 JsonNode#member(Text)
  • 🆕 JsonNode#memberOrNull(Text)
  • 🆕 JsonNode#isMember
  • 🆕 JsonNode#element(Text)
  • 🆕 JsonNode#elementOrNull(Text)
  • 🆎 replaced Iterator with Spliterator for JsonNode#members(boolean) to utilize size knowledge
  • 🆎 replaced Iterator with Spliterator for JsonNode#elements(boolean) to utilize size knowledge
  • 🆎 replaced String with Text in return type of JsonNode#getDeclaration
  • 🆎 replaced String with Text in return type of JsonNode#keys
  • 🆎 replaced String with Text in return type of JsonNode#members
  • 🆎 replaced String with Text in return type of JsonNode#members(boolean)
  • 🆎 replaced String with Text for JSON string and keys of JSON object entries in JsonNode#value
  • 🆎 replaced String with Text in return type of JsonValue#keys
  • 🆎 replaced String with Text in return type of JsonValue#entries
  • 🆎 replaced String with Text in return type of JsonValue#forEach
  • 🆎 JsonValue#path now returns JsonPath (was String)
  • 🆎 Validation.Error#path now is of type JsonPath (was String)
  • 🆎 JsonPathException#path now is of type JsonPath (was String)
  • 🆎 GetListener now accepts a JsonPath (was String)
  • ❌ removed JsonPrimitive#mapNonNull (not needed)

Bugfixes

  • 🐛 fixed serialization of JsonNode and JsonValue (only write JSON and paths)
  • 🐛 fixed JsonNode#get throws JsonTreeException (was JsonPathException) when the operation is used on a non-object node
  • 🐛 fixed JsonNode#getOrNull throws JsonTreeException (was JsonPathException) when the operation is used on a non-object node
  • 🐛 fixed JsonNumber#intValue throws JsonPathException (was NullPointerException) for a node defined JSON null (as stated in javadoc)
  • 🐛 fixed JsonAbstractObject#has throws JsonTreeException when called on an undefined parent

Performance

The general direction is to make the memory footprint as small as possible and rather use some more CPU to skip over sections of the input 1-2 extra times. Profiling shows that the low level scanning of the char[] is very cheap relative to other allocations, exceptions, reflection and alike.

  • using Text views backed by the JSON itself (if possible) avoids a lot of short-lived String allocation
  • JsonPath as chain avoids String and List allocation and O(1) complexity for common ops
  • object key API now being Text based avoids String allocation and improves common key ops runtime complexity
  • JsonNode#size() of arrays and objects no longer causes indexing
  • end index in nodes is now a primitive (avoid wrappers for footprint)
  • JsonNode#value() no longer is cached in a field as the computation has become so cheap it is not with spending the memory
  • JsonVirtualTree now has a node field to cache the JsonNode it represents once it had accessed it (that saves lots of map lookups)
  • computing hashCode() on JsonPath (as used extensively for node lookup) is now O(1) utilizing sampling of a linear interpolation
  • parsing JSON numbers to Java types is now allocation free (except for the returned value of course) and utilizes fast paths for integer numbers and doubles that can be computed from long division
  • avoiding any use of exceptions for logic flow (e.g. test parse, catch return default and such)
  • iterating/streaming array elements and object entries or values now uses size information (if available) to avoid re-allocation from growing collections without size knowledge

@jbee jbee self-assigned this Mar 4, 2026
jbee added 28 commits March 4, 2026 16:58
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
18 New issues
18 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@jbee jbee marked this pull request as ready for review March 12, 2026 15:55
@jbee jbee merged commit 4976c95 into main Mar 12, 2026
2 of 3 checks passed
@jbee jbee deleted the json-validation-classes branch March 12, 2026 15:55
@jbee

jbee commented Mar 12, 2026

Copy link
Copy Markdown
Contributor Author

I went ahead and merged it as it was in a good state. Instead of review I will do a code and feature walk-through once I finished the TODOs I added in another PR. This PR was getting to big 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant