An API to resolve a nested schema using a JSON path? #1275
Replies: 3 comments
-
Here's my initial implementation: import { KindGuard, TSchema } from "@sinclair/typebox";
import * as Type from "@sinclair/typebox/type";
export type JSONPath = `$${string}`;
/**
* Given a TypeBox schema and a JSON path, return the TypeBox schema that
* corresponds to the JSON path.
*/
export function Resolve(
schema: TSchema,
path: JSONPath,
state = parseJSONPath(path),
) {
const part = state.parts[state.index++];
if (!part) {
return schema;
}
if (part === "$") {
return Resolve(schema, path, state);
}
let key: string | undefined;
if (part.charCodeAt(0) === 46 /* . */) {
key = part.slice(1);
} else if (part.charCodeAt(0) === 91 /* [ */) {
if (part.charCodeAt(1) === 39 /* ' */) {
key = part.slice(2, -2);
}
// If no single quote is present, then this is either [*] or a specific
// array index, like [0] or [1].
else if (KindGuard.IsArray(schema)) {
return Resolve(schema.items, path, state);
}
}
if (key != null && KindGuard.IsObject(schema) && key in schema.properties) {
return resolveNestedSchema(schema.properties[key], path, state);
}
return Type.Never();
}
function parseJSONPath(path: JSONPath) {
return {
parts: path.match(/(^\$|\.[^.[\]*]+|\[(?:\*|\d+|'[^']+')\])/gi) ?? [],
index: 0,
};
} Incomplete and untested 😃 |
Beta Was this translation helpful? Give feedback.
-
@aleclarson Hi!
TypeBox offers a few of ways to access interior properties via string. I had originally opted for Json Pointer (RFC 6901) as Json Schema uses this spec for JsonPath does fit the RFC criteria, https://datatracker.ietf.org/doc/rfc9535/ and I quite like the idea of having a XPath equivalent for Json, so I do think this could be something worth looking into. Just for reference though, the current string accessor methods are shown below. Json PointerThe implementation for Json Pointer (RFC 6901) lives under the Value submodule. import { ValuePointer } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
const schema = Type.Object({
array: Type.Array(
Type.Object({
string: Type.String(),
})
),
})
const result = ValuePointer.Get(schema, '/properties/array/items/properties/string')
console.log(result) // { type: 'string', [Symbol(TypeBox.Kind)]: 'String' } Index Access TypesIndex Access types are another option. This approach intends to mirror the semantics of TypeScript and does provide inference support, but is limited to TypeBox types only. import { Type } from '@sinclair/typebox'
import { Syntax } from '@sinclair/typebox/syntax'
const schema = Type.Object({
array: Type.Array(
Type.Object({
string: Type.String(),
})
)
})
const result = Syntax({ schema }, `schema['array'][number]['string']`)
// result is TString I'll do some reading up. I'm wondering if JsonPath could match the interface for ValuePointer or if the interface for it should be limited to only readonly (i.e. Let's chat more on this, I think it could be good to consider adding. Would you be interested in helping to contribute an implementation? |
Beta Was this translation helpful? Give feedback.
-
@aleclarson Hiya, Might move this to discussions. I am open a JSON Path implementation, but probably as part of Still happy to discuss more :) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
https://goessner.net/articles/JsonPath/
Not sure whether this is out of scope, but JSONPath feels like a good fit with JSON Schema.
Essentially, it'd be great to have a utility that takes a TypeBox schema and a string representing a JSON path.
Beta Was this translation helpful? Give feedback.
All reactions