...

Source file src/github.com/go-openapi/jsonpointer/pointer.go

Documentation: github.com/go-openapi/jsonpointer

     1  // Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //   http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // author       sigu-399
    16  // author-github  https://github.com/sigu-399
    17  // author-mail    sigu.399@gmail.com
    18  //
    19  // repository-name  jsonpointer
    20  // repository-desc  An implementation of JSON Pointer - Go language
    21  //
    22  // description    Main and unique file.
    23  //
    24  // created        25-02-2013
    25  
    26  package jsonpointer
    27  
    28  import (
    29  	"encoding/json"
    30  	"errors"
    31  	"fmt"
    32  	"reflect"
    33  	"strconv"
    34  	"strings"
    35  
    36  	"github.com/go-openapi/swag"
    37  )
    38  
    39  const (
    40  	emptyPointer     = ``
    41  	pointerSeparator = `/`
    42  
    43  	invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
    44  	notFound     = `Can't find the pointer in the document`
    45  )
    46  
    47  var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
    48  var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
    49  
    50  // JSONPointable is an interface for structs to implement when they need to customize the
    51  // json pointer process
    52  type JSONPointable interface {
    53  	JSONLookup(string) (any, error)
    54  }
    55  
    56  // JSONSetable is an interface for structs to implement when they need to customize the
    57  // json pointer process
    58  type JSONSetable interface {
    59  	JSONSet(string, any) error
    60  }
    61  
    62  // New creates a new json pointer for the given string
    63  func New(jsonPointerString string) (Pointer, error) {
    64  
    65  	var p Pointer
    66  	err := p.parse(jsonPointerString)
    67  	return p, err
    68  
    69  }
    70  
    71  // Pointer the json pointer reprsentation
    72  type Pointer struct {
    73  	referenceTokens []string
    74  }
    75  
    76  // "Constructor", parses the given string JSON pointer
    77  func (p *Pointer) parse(jsonPointerString string) error {
    78  
    79  	var err error
    80  
    81  	if jsonPointerString != emptyPointer {
    82  		if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
    83  			err = errors.New(invalidStart)
    84  		} else {
    85  			referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
    86  			p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...)
    87  		}
    88  	}
    89  
    90  	return err
    91  }
    92  
    93  // Get uses the pointer to retrieve a value from a JSON document
    94  func (p *Pointer) Get(document any) (any, reflect.Kind, error) {
    95  	return p.get(document, swag.DefaultJSONNameProvider)
    96  }
    97  
    98  // Set uses the pointer to set a value from a JSON document
    99  func (p *Pointer) Set(document any, value any) (any, error) {
   100  	return document, p.set(document, value, swag.DefaultJSONNameProvider)
   101  }
   102  
   103  // GetForToken gets a value for a json pointer token 1 level deep
   104  func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) {
   105  	return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
   106  }
   107  
   108  // SetForToken gets a value for a json pointer token 1 level deep
   109  func SetForToken(document any, decodedToken string, value any) (any, error) {
   110  	return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
   111  }
   112  
   113  func isNil(input any) bool {
   114  	if input == nil {
   115  		return true
   116  	}
   117  
   118  	kind := reflect.TypeOf(input).Kind()
   119  	switch kind { //nolint:exhaustive
   120  	case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
   121  		return reflect.ValueOf(input).IsNil()
   122  	default:
   123  		return false
   124  	}
   125  }
   126  
   127  func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
   128  	rValue := reflect.Indirect(reflect.ValueOf(node))
   129  	kind := rValue.Kind()
   130  	if isNil(node) {
   131  		return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
   132  	}
   133  
   134  	switch typed := node.(type) {
   135  	case JSONPointable:
   136  		r, err := typed.JSONLookup(decodedToken)
   137  		if err != nil {
   138  			return nil, kind, err
   139  		}
   140  		return r, kind, nil
   141  	case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
   142  		return getSingleImpl(*typed, decodedToken, nameProvider)
   143  	}
   144  
   145  	switch kind { //nolint:exhaustive
   146  	case reflect.Struct:
   147  		nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
   148  		if !ok {
   149  			return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
   150  		}
   151  		fld := rValue.FieldByName(nm)
   152  		return fld.Interface(), kind, nil
   153  
   154  	case reflect.Map:
   155  		kv := reflect.ValueOf(decodedToken)
   156  		mv := rValue.MapIndex(kv)
   157  
   158  		if mv.IsValid() {
   159  			return mv.Interface(), kind, nil
   160  		}
   161  		return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
   162  
   163  	case reflect.Slice:
   164  		tokenIndex, err := strconv.Atoi(decodedToken)
   165  		if err != nil {
   166  			return nil, kind, err
   167  		}
   168  		sLength := rValue.Len()
   169  		if tokenIndex < 0 || tokenIndex >= sLength {
   170  			return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
   171  		}
   172  
   173  		elem := rValue.Index(tokenIndex)
   174  		return elem.Interface(), kind, nil
   175  
   176  	default:
   177  		return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
   178  	}
   179  
   180  }
   181  
   182  func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameProvider) error {
   183  	rValue := reflect.Indirect(reflect.ValueOf(node))
   184  
   185  	if ns, ok := node.(JSONSetable); ok { // pointer impl
   186  		return ns.JSONSet(decodedToken, data)
   187  	}
   188  
   189  	if rValue.Type().Implements(jsonSetableType) {
   190  		return node.(JSONSetable).JSONSet(decodedToken, data)
   191  	}
   192  
   193  	switch rValue.Kind() { //nolint:exhaustive
   194  	case reflect.Struct:
   195  		nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
   196  		if !ok {
   197  			return fmt.Errorf("object has no field %q", decodedToken)
   198  		}
   199  		fld := rValue.FieldByName(nm)
   200  		if fld.IsValid() {
   201  			fld.Set(reflect.ValueOf(data))
   202  		}
   203  		return nil
   204  
   205  	case reflect.Map:
   206  		kv := reflect.ValueOf(decodedToken)
   207  		rValue.SetMapIndex(kv, reflect.ValueOf(data))
   208  		return nil
   209  
   210  	case reflect.Slice:
   211  		tokenIndex, err := strconv.Atoi(decodedToken)
   212  		if err != nil {
   213  			return err
   214  		}
   215  		sLength := rValue.Len()
   216  		if tokenIndex < 0 || tokenIndex >= sLength {
   217  			return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
   218  		}
   219  
   220  		elem := rValue.Index(tokenIndex)
   221  		if !elem.CanSet() {
   222  			return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
   223  		}
   224  		elem.Set(reflect.ValueOf(data))
   225  		return nil
   226  
   227  	default:
   228  		return fmt.Errorf("invalid token reference %q", decodedToken)
   229  	}
   230  
   231  }
   232  
   233  func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
   234  
   235  	if nameProvider == nil {
   236  		nameProvider = swag.DefaultJSONNameProvider
   237  	}
   238  
   239  	kind := reflect.Invalid
   240  
   241  	// Full document when empty
   242  	if len(p.referenceTokens) == 0 {
   243  		return node, kind, nil
   244  	}
   245  
   246  	for _, token := range p.referenceTokens {
   247  
   248  		decodedToken := Unescape(token)
   249  
   250  		r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
   251  		if err != nil {
   252  			return nil, knd, err
   253  		}
   254  		node = r
   255  	}
   256  
   257  	rValue := reflect.ValueOf(node)
   258  	kind = rValue.Kind()
   259  
   260  	return node, kind, nil
   261  }
   262  
   263  func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error {
   264  	knd := reflect.ValueOf(node).Kind()
   265  
   266  	if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
   267  		return errors.New("only structs, pointers, maps and slices are supported for setting values")
   268  	}
   269  
   270  	if nameProvider == nil {
   271  		nameProvider = swag.DefaultJSONNameProvider
   272  	}
   273  
   274  	// Full document when empty
   275  	if len(p.referenceTokens) == 0 {
   276  		return nil
   277  	}
   278  
   279  	lastI := len(p.referenceTokens) - 1
   280  	for i, token := range p.referenceTokens {
   281  		isLastToken := i == lastI
   282  		decodedToken := Unescape(token)
   283  
   284  		if isLastToken {
   285  
   286  			return setSingleImpl(node, data, decodedToken, nameProvider)
   287  		}
   288  
   289  		rValue := reflect.Indirect(reflect.ValueOf(node))
   290  		kind := rValue.Kind()
   291  
   292  		if rValue.Type().Implements(jsonPointableType) {
   293  			r, err := node.(JSONPointable).JSONLookup(decodedToken)
   294  			if err != nil {
   295  				return err
   296  			}
   297  			fld := reflect.ValueOf(r)
   298  			if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
   299  				node = fld.Addr().Interface()
   300  				continue
   301  			}
   302  			node = r
   303  			continue
   304  		}
   305  
   306  		switch kind { //nolint:exhaustive
   307  		case reflect.Struct:
   308  			nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
   309  			if !ok {
   310  				return fmt.Errorf("object has no field %q", decodedToken)
   311  			}
   312  			fld := rValue.FieldByName(nm)
   313  			if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
   314  				node = fld.Addr().Interface()
   315  				continue
   316  			}
   317  			node = fld.Interface()
   318  
   319  		case reflect.Map:
   320  			kv := reflect.ValueOf(decodedToken)
   321  			mv := rValue.MapIndex(kv)
   322  
   323  			if !mv.IsValid() {
   324  				return fmt.Errorf("object has no key %q", decodedToken)
   325  			}
   326  			if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
   327  				node = mv.Addr().Interface()
   328  				continue
   329  			}
   330  			node = mv.Interface()
   331  
   332  		case reflect.Slice:
   333  			tokenIndex, err := strconv.Atoi(decodedToken)
   334  			if err != nil {
   335  				return err
   336  			}
   337  			sLength := rValue.Len()
   338  			if tokenIndex < 0 || tokenIndex >= sLength {
   339  				return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
   340  			}
   341  
   342  			elem := rValue.Index(tokenIndex)
   343  			if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
   344  				node = elem.Addr().Interface()
   345  				continue
   346  			}
   347  			node = elem.Interface()
   348  
   349  		default:
   350  			return fmt.Errorf("invalid token reference %q", decodedToken)
   351  		}
   352  
   353  	}
   354  
   355  	return nil
   356  }
   357  
   358  // DecodedTokens returns the decoded tokens
   359  func (p *Pointer) DecodedTokens() []string {
   360  	result := make([]string, 0, len(p.referenceTokens))
   361  	for _, t := range p.referenceTokens {
   362  		result = append(result, Unescape(t))
   363  	}
   364  	return result
   365  }
   366  
   367  // IsEmpty returns true if this is an empty json pointer
   368  // this indicates that it points to the root document
   369  func (p *Pointer) IsEmpty() bool {
   370  	return len(p.referenceTokens) == 0
   371  }
   372  
   373  // Pointer to string representation function
   374  func (p *Pointer) String() string {
   375  
   376  	if len(p.referenceTokens) == 0 {
   377  		return emptyPointer
   378  	}
   379  
   380  	pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
   381  
   382  	return pointerString
   383  }
   384  
   385  func (p *Pointer) Offset(document string) (int64, error) {
   386  	dec := json.NewDecoder(strings.NewReader(document))
   387  	var offset int64
   388  	for _, ttk := range p.DecodedTokens() {
   389  		tk, err := dec.Token()
   390  		if err != nil {
   391  			return 0, err
   392  		}
   393  		switch tk := tk.(type) {
   394  		case json.Delim:
   395  			switch tk {
   396  			case '{':
   397  				offset, err = offsetSingleObject(dec, ttk)
   398  				if err != nil {
   399  					return 0, err
   400  				}
   401  			case '[':
   402  				offset, err = offsetSingleArray(dec, ttk)
   403  				if err != nil {
   404  					return 0, err
   405  				}
   406  			default:
   407  				return 0, fmt.Errorf("invalid token %#v", tk)
   408  			}
   409  		default:
   410  			return 0, fmt.Errorf("invalid token %#v", tk)
   411  		}
   412  	}
   413  	return offset, nil
   414  }
   415  
   416  func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) {
   417  	for dec.More() {
   418  		offset := dec.InputOffset()
   419  		tk, err := dec.Token()
   420  		if err != nil {
   421  			return 0, err
   422  		}
   423  		switch tk := tk.(type) {
   424  		case json.Delim:
   425  			switch tk {
   426  			case '{':
   427  				if err = drainSingle(dec); err != nil {
   428  					return 0, err
   429  				}
   430  			case '[':
   431  				if err = drainSingle(dec); err != nil {
   432  					return 0, err
   433  				}
   434  			}
   435  		case string:
   436  			if tk == decodedToken {
   437  				return offset, nil
   438  			}
   439  		default:
   440  			return 0, fmt.Errorf("invalid token %#v", tk)
   441  		}
   442  	}
   443  	return 0, fmt.Errorf("token reference %q not found", decodedToken)
   444  }
   445  
   446  func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) {
   447  	idx, err := strconv.Atoi(decodedToken)
   448  	if err != nil {
   449  		return 0, fmt.Errorf("token reference %q is not a number: %v", decodedToken, err)
   450  	}
   451  	var i int
   452  	for i = 0; i < idx && dec.More(); i++ {
   453  		tk, err := dec.Token()
   454  		if err != nil {
   455  			return 0, err
   456  		}
   457  
   458  		if delim, isDelim := tk.(json.Delim); isDelim {
   459  			switch delim {
   460  			case '{':
   461  				if err = drainSingle(dec); err != nil {
   462  					return 0, err
   463  				}
   464  			case '[':
   465  				if err = drainSingle(dec); err != nil {
   466  					return 0, err
   467  				}
   468  			}
   469  		}
   470  	}
   471  
   472  	if !dec.More() {
   473  		return 0, fmt.Errorf("token reference %q not found", decodedToken)
   474  	}
   475  	return dec.InputOffset(), nil
   476  }
   477  
   478  // drainSingle drains a single level of object or array.
   479  // The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed.
   480  func drainSingle(dec *json.Decoder) error {
   481  	for dec.More() {
   482  		tk, err := dec.Token()
   483  		if err != nil {
   484  			return err
   485  		}
   486  		if delim, isDelim := tk.(json.Delim); isDelim {
   487  			switch delim {
   488  			case '{':
   489  				if err = drainSingle(dec); err != nil {
   490  					return err
   491  				}
   492  			case '[':
   493  				if err = drainSingle(dec); err != nil {
   494  					return err
   495  				}
   496  			}
   497  		}
   498  	}
   499  
   500  	// Consumes the ending delim
   501  	if _, err := dec.Token(); err != nil {
   502  		return err
   503  	}
   504  	return nil
   505  }
   506  
   507  // Specific JSON pointer encoding here
   508  // ~0 => ~
   509  // ~1 => /
   510  // ... and vice versa
   511  
   512  const (
   513  	encRefTok0 = `~0`
   514  	encRefTok1 = `~1`
   515  	decRefTok0 = `~`
   516  	decRefTok1 = `/`
   517  )
   518  
   519  // Unescape unescapes a json pointer reference token string to the original representation
   520  func Unescape(token string) string {
   521  	step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1)
   522  	step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0)
   523  	return step2
   524  }
   525  
   526  // Escape escapes a pointer reference token string
   527  func Escape(token string) string {
   528  	step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0)
   529  	step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1)
   530  	return step2
   531  }
   532  

View as plain text