pointer.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
  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. // author xeipuuv
  15. // author-github https://github.com/xeipuuv
  16. // author-mail [email protected]
  17. //
  18. // repository-name gojsonpointer
  19. // repository-desc An implementation of JSON Pointer - Go language
  20. //
  21. // description Main and unique file.
  22. //
  23. // created 25-02-2013
  24. package gojsonpointer
  25. import (
  26. "errors"
  27. "fmt"
  28. "reflect"
  29. "strconv"
  30. "strings"
  31. )
  32. const (
  33. const_empty_pointer = ``
  34. const_pointer_separator = `/`
  35. const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
  36. )
  37. type implStruct struct {
  38. mode string // "SET" or "GET"
  39. inDocument interface{}
  40. setInValue interface{}
  41. getOutNode interface{}
  42. getOutKind reflect.Kind
  43. outError error
  44. }
  45. type JsonPointer struct {
  46. referenceTokens []string
  47. }
  48. // NewJsonPointer parses the given string JSON pointer and returns an object
  49. func NewJsonPointer(jsonPointerString string) (p JsonPointer, err error) {
  50. // Pointer to the root of the document
  51. if len(jsonPointerString) == 0 {
  52. // Keep referenceTokens nil
  53. return
  54. }
  55. if jsonPointerString[0] != '/' {
  56. return p, errors.New(const_invalid_start)
  57. }
  58. p.referenceTokens = strings.Split(jsonPointerString[1:], const_pointer_separator)
  59. return
  60. }
  61. // Uses the pointer to retrieve a value from a JSON document
  62. func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
  63. is := &implStruct{mode: "GET", inDocument: document}
  64. p.implementation(is)
  65. return is.getOutNode, is.getOutKind, is.outError
  66. }
  67. // Uses the pointer to update a value from a JSON document
  68. func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
  69. is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
  70. p.implementation(is)
  71. return document, is.outError
  72. }
  73. // Both Get and Set functions use the same implementation to avoid code duplication
  74. func (p *JsonPointer) implementation(i *implStruct) {
  75. kind := reflect.Invalid
  76. // Full document when empty
  77. if len(p.referenceTokens) == 0 {
  78. i.getOutNode = i.inDocument
  79. i.outError = nil
  80. i.getOutKind = kind
  81. i.outError = nil
  82. return
  83. }
  84. node := i.inDocument
  85. for ti, token := range p.referenceTokens {
  86. isLastToken := ti == len(p.referenceTokens)-1
  87. switch v := node.(type) {
  88. case map[string]interface{}:
  89. decodedToken := decodeReferenceToken(token)
  90. if _, ok := v[decodedToken]; ok {
  91. node = v[decodedToken]
  92. if isLastToken && i.mode == "SET" {
  93. v[decodedToken] = i.setInValue
  94. }
  95. } else {
  96. i.outError = fmt.Errorf("Object has no key '%s'", decodedToken)
  97. i.getOutKind = reflect.Map
  98. i.getOutNode = nil
  99. return
  100. }
  101. case []interface{}:
  102. tokenIndex, err := strconv.Atoi(token)
  103. if err != nil {
  104. i.outError = fmt.Errorf("Invalid array index '%s'", token)
  105. i.getOutKind = reflect.Slice
  106. i.getOutNode = nil
  107. return
  108. }
  109. if tokenIndex < 0 || tokenIndex >= len(v) {
  110. i.outError = fmt.Errorf("Out of bound array[0,%d] index '%d'", len(v), tokenIndex)
  111. i.getOutKind = reflect.Slice
  112. i.getOutNode = nil
  113. return
  114. }
  115. node = v[tokenIndex]
  116. if isLastToken && i.mode == "SET" {
  117. v[tokenIndex] = i.setInValue
  118. }
  119. default:
  120. i.outError = fmt.Errorf("Invalid token reference '%s'", token)
  121. i.getOutKind = reflect.ValueOf(node).Kind()
  122. i.getOutNode = nil
  123. return
  124. }
  125. }
  126. i.getOutNode = node
  127. i.getOutKind = reflect.ValueOf(node).Kind()
  128. i.outError = nil
  129. }
  130. // Pointer to string representation function
  131. func (p *JsonPointer) String() string {
  132. if len(p.referenceTokens) == 0 {
  133. return const_empty_pointer
  134. }
  135. pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
  136. return pointerString
  137. }
  138. // Specific JSON pointer encoding here
  139. // ~0 => ~
  140. // ~1 => /
  141. // ... and vice versa
  142. func decodeReferenceToken(token string) string {
  143. step1 := strings.Replace(token, `~1`, `/`, -1)
  144. step2 := strings.Replace(step1, `~0`, `~`, -1)
  145. return step2
  146. }
  147. func encodeReferenceToken(token string) string {
  148. step1 := strings.Replace(token, `~`, `~0`, -1)
  149. step2 := strings.Replace(step1, `/`, `~1`, -1)
  150. return step2
  151. }