Skip to content

deepObject: bracket-indexed arrays and nested objects - #111

Open
tanmaykm wants to merge 1 commit into
mainfrom
deepobject-bracket-paths
Open

deepObject: bracket-indexed arrays and nested objects#111
tanmaykm wants to merge 1 commit into
mainfrom
deepobject-bracket-paths

Conversation

@tanmaykm

@tanmaykm tanmaykm commented Sep 5, 2026

Copy link
Copy Markdown
Member

Fixes #110.

Summary

deepObject query parameters with array schemas or nested object/array values worked in 0.2 (added in #78) but not in 1.0.0: strict rejected the schema, permissive planned it and then failed every request, and the client encoder threw on nested values. This restores end-to-end support using the bracket convention that qs, Rack and PHP produce, as a documented extension accepted at strict.

filters[0][field]=severity&filters[0][values][0]=error&filters[0][values][1]=warning
sorts[0]=-created_at&sorts[1]=%2Bname

Changes

  • Planning (src/planning.jl): array schemas under deepObject plan at strict without diagnostics. Only scalar schemas still emit invalid_deep_object_schema (error at strict, warning at permissive).
  • Client encoder (src/runtime.jl): nested dicts and vectors flatten recursively into indexed bracket paths. Arrays now encode as name[0]=… rather than name[]=…, matching the 0.2 encoder and the qs default. The multipart deepObject encoder shares the helper.
  • Server decoder (src/servergen.jl + src/runtime.jl): full bracket paths (including percent-encoded brackets and name[] appends) build a tree, and a schema-guided pass rebuilds it in the declared shape before validation. Integer keys become array indices only where the schema expects an array, so an additionalProperties object keeps "0" as a string key. Leaves stay text where the schema says string, so sorts[0]=2024 is no longer read as an integer. $ref, allOf, oneOf and anyOf are followed.
  • Schema engine: adds a generic non-throwing trysubschema accessor.
  • Docs: new "deepObject bracket paths" section in the servers manual, linked from the clients manual; the boundary page now lists only scalar deepObject as a permissive case.
  • Version: 1.1.0, since strict mode accepts new input.

Decisions worth a look

  1. Arrays of scalars now encode indexed (expand[0]=a&expand[1]=b) instead of expand[]=a&expand[]=b. The server accepts both forms; this is a visible client wire change from 1.0.0.
  2. Arrays are accepted at strict with no diagnostic. An informational note would be a one-line change in the planner if preferred.

Verification

  • The reproduction from deepObject: bracket-indexed arrays and nested objects (supported in 0.2 via #78) no longer work in 1.0 #110 returns 200 with typed models in strict mode for both spec variants, and the client emits filters[0][field]=… pairs.
  • Full suite on Julia 1.12.7 passes, including a new server-level testset covering the qs wire format, [] appends, encoded brackets, sparse indices, the array-vs-object-key decision, string leaves, and 400s for invalid shapes. Julia 1.10 was not available locally.
  • OPENAPI_CORPUS_TESTS=small passes. Docs build passes and the new cross-reference resolves.
  • The checked-in JuliaC --trim=safe workload still passes. Note that the request/response runtime paths (query encoding, JSON decode, schema validation) were not trim-safe before this change and are not made so here; a widened trim workload fails the same way before and after.

Restore the 0.2 behaviour for deepObject query parameters whose values are
arrays or nested objects, using the bracket convention that qs, Rack and PHP
produce (`filters[0][field]=v`, `sorts[0]=a&sorts[1]=b`, `name[]=v`).

- planning: accept array schemas under deepObject at strict; only scalar
  schemas remain an error (strict) / warning (permissive).
- runtime client encoder: flatten nested dicts/vectors recursively into
  indexed bracket paths instead of throwing or emitting `name[]`.
  The multipart deepObject encoder shares the helper.
- server decoder: parse full bracket paths (percent-encoded brackets and
  `[]` appends included) into a tree, then rebuild it in the shape the
  parameter schema declares before validation: integer keys become array
  indices only where the schema expects an array, leaves stay strings where
  the schema says string, and $ref/allOf/oneOf/anyOf are followed.
- schema engine: add non-throwing `trysubschema`.
- docs: describe the extension; boundary page now lists only scalar
  deepObject as a permissive case.
- version 1.1.0.
@tanmaykm
tanmaykm marked this pull request as draft September 5, 2026 19:03
@tanmaykm
tanmaykm marked this pull request as ready for review September 5, 2026 19:13
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.

deepObject: bracket-indexed arrays and nested objects (supported in 0.2 via #78) no longer work in 1.0

1 participant