1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
package cli
import (
"encoding/json"
"io"
)
type jsonStream struct {
dec *json.Decoder
path []any
states []int
}
func newJSONStream(dec *json.Decoder) *jsonStream {
return &jsonStream{dec: dec, states: []int{jsonStateTopValue}, path: []any{}}
}
const (
jsonStateTopValue = iota
jsonStateArrayStart
jsonStateArrayValue
jsonStateArrayEnd
jsonStateArrayEmptyEnd
jsonStateObjectStart
jsonStateObjectKey
jsonStateObjectValue
jsonStateObjectEnd
jsonStateObjectEmptyEnd
)
func (s *jsonStream) next() (any, error) {
switch s.states[len(s.states)-1] {
case jsonStateArrayEnd, jsonStateObjectEnd:
s.path = s.path[:len(s.path)-1]
fallthrough
case jsonStateArrayEmptyEnd, jsonStateObjectEmptyEnd:
s.states = s.states[:len(s.states)-1]
}
if s.dec.More() {
switch s.states[len(s.states)-1] {
case jsonStateArrayValue:
s.path[len(s.path)-1] = s.path[len(s.path)-1].(int) + 1
case jsonStateObjectValue:
s.path = s.path[:len(s.path)-1]
}
}
for {
token, err := s.dec.Token()
if err != nil {
if err == io.EOF && s.states[len(s.states)-1] != jsonStateTopValue {
err = io.ErrUnexpectedEOF
}
return nil, err
}
if d, ok := token.(json.Delim); ok {
switch d {
case '[', '{':
switch s.states[len(s.states)-1] {
case jsonStateArrayStart:
s.states[len(s.states)-1] = jsonStateArrayValue
case jsonStateObjectKey:
s.states[len(s.states)-1] = jsonStateObjectValue
}
if d == '[' {
s.states = append(s.states, jsonStateArrayStart)
s.path = append(s.path, 0)
} else {
s.states = append(s.states, jsonStateObjectStart)
}
case ']':
if s.states[len(s.states)-1] == jsonStateArrayStart {
s.states[len(s.states)-1] = jsonStateArrayEmptyEnd
s.path = s.path[:len(s.path)-1]
return []any{s.copyPath(), []any{}}, nil
}
s.states[len(s.states)-1] = jsonStateArrayEnd
return []any{s.copyPath()}, nil
case '}':
if s.states[len(s.states)-1] == jsonStateObjectStart {
s.states[len(s.states)-1] = jsonStateObjectEmptyEnd
return []any{s.copyPath(), map[string]any{}}, nil
}
s.states[len(s.states)-1] = jsonStateObjectEnd
return []any{s.copyPath()}, nil
default:
panic(d)
}
} else {
switch s.states[len(s.states)-1] {
case jsonStateArrayStart:
s.states[len(s.states)-1] = jsonStateArrayValue
fallthrough
case jsonStateArrayValue:
return []any{s.copyPath(), token}, nil
case jsonStateObjectStart, jsonStateObjectValue:
s.states[len(s.states)-1] = jsonStateObjectKey
s.path = append(s.path, token)
case jsonStateObjectKey:
s.states[len(s.states)-1] = jsonStateObjectValue
return []any{s.copyPath(), token}, nil
default:
s.states[len(s.states)-1] = jsonStateTopValue
return []any{s.copyPath(), token}, nil
}
}
}
}
func (s *jsonStream) copyPath() []any {
path := make([]any, len(s.path))
copy(path, s.path)
return path
}
|