-
Notifications
You must be signed in to change notification settings - Fork 179
An API to resolve a nested schema using a JSON path? #1254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 😃 |
@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? |
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.
The text was updated successfully, but these errors were encountered: