114 lines
2.9 KiB
Go
114 lines
2.9 KiB
Go
package main
|
|
|
|
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
|
|
}
|