-
Notifications
You must be signed in to change notification settings - Fork 23
Description
Summary
Adopt RFC 6901 (JSON Pointer) for the paths that identify form fields (and the corresponding JSON properties) instead of the current dot-notation (Lodash-style property path). Paths would be built, stored, and parsed as JSON Pointers (e.g. /data/items/0/name), with a small adapter so the rest of the form logic stays unchanged.
Current behavior
Form field paths are built in annotateProp() (nx/blocks/form/utils/utils.js) as dot-separated strings with bracket indices, e.g.:
datadata.titledata.items.[0].name
They are used as the name attribute on form controls and written back via setValueByPath(), which splits on . and handles [n]. This follows a known, widely used convention in JS app code (e.g. Lodash _.get/_.set), but there is no formal standard for it, and no defined escaping for property names containing ., [, ], or ~, so certain keys can break or be ambiguous.
Proposed change
- Represent paths as RFC 6901 JSON Pointers (slash-separated, with
~0/~1encoding where needed). - Build paths in
annotateProp()(or a small helper) as JSON Pointers (e.g./data,/data/items/0/name). - Parse paths with an RFC 6901–compliant implementation (library or small in-house module) for get/set instead of the current
setValueByPath()string-splitting logic. - Keep the same public behavior: same form fields, same
updateevent contract; only the internal path format and parsing change. Optionally keep accepting current dot-notation and normalize to JSON Pointer for backward compatibility during a transition.
Why RFC 6901 fits our stack
RFC 6901 (JSON Pointer) is used primarily in specs and tooling (JSON Schema, OpenAPI, JSON Patch), not in everyday app path strings. Our app already relies on JSON Schema and validation; form data is schema-driven and validated against those schemas. Using JSON Pointer for field paths aligns with that world: the same path format appears in schema references, validation errors, and patches, so one consistent notation serves forms, validation, and tooling.
Benefits
- Standard, stable format: Paths have a single, well-defined meaning and encoding (RFC 6901).
- Ecosystem alignment: Same format as JSON Schema
$ref, JSON Patch (RFC 6902), and many API specs; easier to integrate validation, patches, or tooling—and consistent with our existing use of JSON Schema and validation. - Robust edge cases: Keys containing
.,/,~,[,]are handled via the RFC’s encoding rules instead of ad‑hoc logic. - Less custom code: Path parsing and encoding can rely on a well-tested library (or a minimal compliant implementation) instead of maintaining custom regex/split logic.
- Future-proof: Aligns with the broader JSON ecosystem and reduces the risk of path-related bugs as schemas and property names evolve.
Note on custom implementation vs. dependency
Even if you do not add a dependency and implement JSON Pointer yourself, following RFC 6901 is still more future-proof than the current dot-notation:
- Learnable from a standard: Any developer can look up RFC 6901 and implement or review the behavior. The rules are fixed and documented in one place.
- Widely known: JSON Pointer is used in JSON Schema, OpenAPI, and JSON Patch, so many developers already know it or can recognize it quickly.
- Stable spec: A custom implementation that follows the RFC gives you a single, well-defined path format and encoding; you avoid inventing and maintaining your own path syntax and edge-case rules.
So adopting the RFC convention (with or without a library) improves long-term maintainability and reduces the risk of path-related bugs, and stays consistent with the spec/tooling side of our stack (JSON Schema, validation).
Examples
Current (Lodash-style dot-notation)
| Location in JSON | Current path string |
|---|---|
Root data |
data |
data.title |
data.title |
First item’s name in data.items |
data.items.[0].name |
Proposed (RFC 6901 JSON Pointer)
| Location in JSON | JSON Pointer |
|---|---|
Root data |
/data |
data.title |
/data/title |
First item’s name in data.items |
/data/items/0/name |
Suggested implementation steps
-
Use an existing RFC 6901–compliant library, or implement get/set following the RFC.
For example, use @hyperjump/json-pointer (implements RFC 6901, Node/browsers,get/set/assign/unset/pointerSegments/append— hyperjump-io/json-pointer, MIT):npm i @hyperjump/json-pointer
Alternatively, implement get/set following RFC 6901 with no new dependency; the RFC is enough for a correct, future-proof path format.
-
Introduce a thin wrapper (if using a library) around
@hyperjump/json-pointer: e.g.getByPointer(obj, pointer),setByPointer(obj, pointer, value), and a helper to build pointer strings from the segments used inannotateProp()(e.g./data,/data/items/0/name). -
In
annotateProp(), build the path as a JSON Pointer string (e.g./data,/data/items/0/name) and store it on each annotated node (e.g.item.path). -
Replace the current
setValueByPath(this._json, name, value)inFormModel.updateProperty()with the library’s set (or your RFC-based implementation), using the samename(now a JSON Pointer). -
Keep form controls using that
nameand theupdateevent payload as-is; only the internal format ofnamechanges to a pointer. -
Add/update tests for pointers with special characters (e.g.
.,/,~) and array indices. If anything external still expects the old dot-notation, add a short compatibility layer (e.g. convert old → pointer once) or document the breaking change.