decoder.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package jsonpath
  2. import (
  3. "encoding/json"
  4. "io"
  5. )
  6. // KeyString is returned from Decoder.Token to represent each key in a JSON object value.
  7. type KeyString string
  8. // Decoder extends the Go runtime's encoding/json.Decoder to support navigating in a stream of JSON tokens.
  9. type Decoder struct {
  10. json.Decoder
  11. path JsonPath
  12. context jsonContext
  13. }
  14. // NewDecoder creates a new instance of the extended JSON Decoder.
  15. func NewDecoder(r io.Reader) *Decoder {
  16. return &Decoder{Decoder: *json.NewDecoder(r)}
  17. }
  18. // SeekTo causes the Decoder to move forward to a given path in the JSON structure.
  19. //
  20. // The path argument must consist of strings or integers. Each string specifies an JSON object key, and
  21. // each integer specifies an index into a JSON array.
  22. //
  23. // Consider the JSON structure
  24. //
  25. // { "a": [0,"s",12e4,{"b":0,"v":35} ] }
  26. //
  27. // SeekTo("a",3,"v") will move to the value referenced by the "a" key in the current object,
  28. // followed by a move to the 4th value (index 3) in the array, followed by a move to the value at key "v".
  29. // In this example, a subsequent call to the decoder's Decode() would unmarshal the value 35.
  30. //
  31. // SeekTo returns a boolean value indicating whether a match was found.
  32. //
  33. // Decoder is intended to be used with a stream of tokens. As a result it navigates forward only.
  34. func (d *Decoder) SeekTo(path ...interface{}) (bool, error) {
  35. if len(path) == 0 {
  36. return len(d.path) == 0, nil
  37. }
  38. last := len(path) - 1
  39. if i, ok := path[last].(int); ok {
  40. path[last] = i - 1
  41. }
  42. for {
  43. if d.path.Equal(path) {
  44. return true, nil
  45. }
  46. _, err := d.Token()
  47. if err == io.EOF {
  48. return false, nil
  49. } else if err != nil {
  50. return false, err
  51. }
  52. }
  53. }
  54. // Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v. This is
  55. // equivalent to encoding/json.Decode().
  56. func (d *Decoder) Decode(v interface{}) error {
  57. switch d.context {
  58. case objValue:
  59. d.context = objKey
  60. break
  61. case arrValue:
  62. d.path.incTop()
  63. break
  64. }
  65. return d.Decoder.Decode(v)
  66. }
  67. // Path returns a slice of string and/or int values representing the path from the root of the JSON object to the
  68. // position of the most-recently parsed token.
  69. func (d *Decoder) Path() JsonPath {
  70. p := make(JsonPath, len(d.path))
  71. copy(p, d.path)
  72. return p
  73. }
  74. // Token is equivalent to the Token() method on json.Decoder. The primary difference is that it distinguishes
  75. // between strings that are keys and and strings that are values. String tokens that are object keys are returned as a
  76. // KeyString rather than as a native string.
  77. func (d *Decoder) Token() (json.Token, error) {
  78. t, err := d.Decoder.Token()
  79. if err != nil {
  80. return t, err
  81. }
  82. if t == nil {
  83. switch d.context {
  84. case objValue:
  85. d.context = objKey
  86. break
  87. case arrValue:
  88. d.path.incTop()
  89. break
  90. }
  91. return t, err
  92. }
  93. switch t := t.(type) {
  94. case json.Delim:
  95. switch t {
  96. case json.Delim('{'):
  97. if d.context == arrValue {
  98. d.path.incTop()
  99. }
  100. d.path.push("")
  101. d.context = objKey
  102. break
  103. case json.Delim('}'):
  104. d.path.pop()
  105. d.context = d.path.inferContext()
  106. break
  107. case json.Delim('['):
  108. if d.context == arrValue {
  109. d.path.incTop()
  110. }
  111. d.path.push(-1)
  112. d.context = arrValue
  113. break
  114. case json.Delim(']'):
  115. d.path.pop()
  116. d.context = d.path.inferContext()
  117. break
  118. }
  119. case float64, json.Number, bool:
  120. switch d.context {
  121. case objValue:
  122. d.context = objKey
  123. break
  124. case arrValue:
  125. d.path.incTop()
  126. break
  127. }
  128. break
  129. case string:
  130. switch d.context {
  131. case objKey:
  132. d.path.nameTop(t)
  133. d.context = objValue
  134. return KeyString(t), err
  135. case objValue:
  136. d.context = objKey
  137. case arrValue:
  138. d.path.incTop()
  139. }
  140. break
  141. }
  142. return t, err
  143. }
  144. // Scan moves forward over the JSON stream consuming all the tokens at the current level (current object, current array)
  145. // invoking each matching PathAction along the way.
  146. //
  147. // Scan returns true if there are more contiguous values to scan (for example in an array).
  148. func (d *Decoder) Scan(ext *PathActions) (bool, error) {
  149. rootPath := d.Path()
  150. // If this is an array path, increment the root path in our local copy.
  151. if rootPath.inferContext() == arrValue {
  152. rootPath.incTop()
  153. }
  154. for {
  155. // advance the token position
  156. _, err := d.Token()
  157. if err != nil {
  158. return false, err
  159. }
  160. match:
  161. var relPath JsonPath
  162. // capture the new JSON path
  163. path := d.Path()
  164. if len(path) > len(rootPath) {
  165. // capture the path relative to where the scan started
  166. relPath = path[len(rootPath):]
  167. } else {
  168. // if the path is not longer than the root, then we are done with this scan
  169. // return boolean flag indicating if there are more items to scan at the same level
  170. return d.Decoder.More(), nil
  171. }
  172. // match the relative path against the path actions
  173. if node := ext.node.match(relPath); node != nil {
  174. if node.action != nil {
  175. // we have a match so execute the action
  176. err = node.action(d)
  177. if err != nil {
  178. return d.Decoder.More(), err
  179. }
  180. // The action may have advanced the decoder. If we are in an array, advancing it further would
  181. // skip tokens. So, if we are scanning an array, jump to the top without advancing the token.
  182. if d.path.inferContext() == arrValue && d.Decoder.More() {
  183. goto match
  184. }
  185. }
  186. }
  187. }
  188. }