// Copyright 2025 The JSON Schema Go Project Authors. All rights reserved.// Use of this source code is governed by an MIT-style// license that can be found in the LICENSE file.// This file implements JSON Pointers.// A JSON Pointer is a path that refers to one JSON value within another.// If the path is empty, it refers to the root value.// Otherwise, it is a sequence of slash-prefixed strings, like "/points/1/x",// selecting successive properties (for JSON objects) or items (for JSON arrays).// For example, when applied to this JSON value:// {// "points": [// {"x": 1, "y": 2},// {"x": 3, "y": 4}// ]// }//// the JSON Pointer "/points/1/x" refers to the number 3.// See the spec at https://datatracker.ietf.org/doc/html/rfc6901.package jsonschemaimport ()var ( jsonPointerEscaper = strings.NewReplacer("~", "~0", "/", "~1") jsonPointerUnescaper = strings.NewReplacer("~0", "~", "~1", "/"))func escapeJSONPointerSegment( string) string {returnjsonPointerEscaper.Replace()}func unescapeJSONPointerSegment( string) string {returnjsonPointerUnescaper.Replace()}// parseJSONPointer splits a JSON Pointer into a sequence of segments. It doesn't// convert strings to numbers, because that depends on the traversal: a segment// is treated as a number when applied to an array, but a string when applied to// an object. See section 4 of the spec.func parseJSONPointer( string) ( []string, error) {if == "" {returnnil, nil }if [0] != '/' {returnnil, fmt.Errorf("JSON Pointer %q does not begin with '/'", ) }// Unlike file paths, consecutive slashes are not coalesced. // Split is nicer than Cut here, because it gets a final "/" right. = strings.Split([1:], "/")ifstrings.Contains(, "~") {// Undo the simple escaping rules that allow one to include a slash in a segment.for := range { [] = unescapeJSONPointerSegment([]) } }return , nil}// dereferenceJSONPointer returns the Schema that sptr points to within s,// or an error if none.// This implementation suffices for JSON Schema: pointers are applied only to Schemas,// and refer only to Schemas.func dereferenceJSONPointer( *Schema, string) ( *Schema, error) {deferwrapf(&, "JSON Pointer %q", ) , := parseJSONPointer()if != nil {returnnil, } := reflect.ValueOf()for , := range {switch .Kind() {casereflect.Pointer: = .Elem()if !.IsValid() {returnnil, errors.New("navigated to nil reference") }fallthrough// if valid, can only be a pointer to a Schemacasereflect.Struct:// The segment must refer to a field in a Schema.if .Type() != reflect.TypeFor[Schema]() {returnnil, fmt.Errorf("navigated to non-Schema %s", .Type()) } = lookupSchemaField(, )if !.IsValid() {returnnil, fmt.Errorf("no schema field %q", ) }casereflect.Slice, reflect.Array:// The segment must be an integer without leading zeroes that refers to an item in the // slice or array.if == "-" {returnnil, errors.New("the JSON Pointer array segment '-' is not supported") }iflen() > 1 && [0] == '0' {returnnil, fmt.Errorf("segment %q has leading zeroes", ) } , := strconv.Atoi()if != nil {returnnil, fmt.Errorf("invalid int: %q", ) }if < 0 || >= .Len() {returnnil, fmt.Errorf("index %d is out of bounds for array of length %d", , .Len()) } = .Index()// Cannot be invalid.casereflect.Map:// The segment must be a key in the map. = .MapIndex(reflect.ValueOf())if !.IsValid() {returnnil, fmt.Errorf("no key %q in map", ) }default:returnnil, fmt.Errorf("value %s (%s) is not a schema, slice or map", , .Type()) } }if , := .Interface().(*Schema); {return , nil }returnnil, fmt.Errorf("does not refer to a schema, but to a %s", .Type())}// lookupSchemaField returns the value of the field with the given name in v,// or the zero value if there is no such field or it is not of type Schema or *Schema.func lookupSchemaField( reflect.Value, string) reflect.Value {if == "type" {// The "type" keyword may refer to Type or Types. // At most one will be non-zero.if := .FieldByName("Type"); !.IsZero() {return }return .FieldByName("Types") }if == "items" {// The "items" keyword refers to the "union type" that is either a schema or a schema array. // Implemented using the Items representing the schema and ItemsArray for the schema array.if := .FieldByName("Items"); .IsValid() && !.IsNil() {return }return .FieldByName("ItemsArray") }if == "dependencies" {// The "dependencies" keyword refers to both DependencyStrings and DependencySchemas maps. // The value on schemaFieldMap is not garanteed to be DependencySchemas which we want // for pointer dereference. So we use FieldByName to get the DependencySchemas map.return .FieldByName("DependencySchemas") }if , := schemaFieldMap[]; {return .FieldByIndex(.Index) }returnreflect.Value{}}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.