Begin to implement jsonpath
This commit is contained in:
182
processor/jsonpath/jsonpath.go
Normal file
182
processor/jsonpath/jsonpath.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// JSONStep represents a single step in a JSONPath query
|
||||
type JSONStep struct {
|
||||
Type StepType
|
||||
Key string // For Child/RecursiveDescent
|
||||
Index int // For Index (use -1 for wildcard "*")
|
||||
}
|
||||
|
||||
type StepType int
|
||||
|
||||
const (
|
||||
RootStep StepType = iota
|
||||
ChildStep
|
||||
RecursiveDescentStep
|
||||
WildcardStep
|
||||
IndexStep
|
||||
)
|
||||
|
||||
func ParseJSONPath(path string) ([]JSONStep, error) {
|
||||
if len(path) == 0 || path[0] != '$' {
|
||||
return nil, fmt.Errorf("path must start with $")
|
||||
}
|
||||
|
||||
path = path[1:]
|
||||
steps := []JSONStep{}
|
||||
i := 0
|
||||
|
||||
for i < len(path) {
|
||||
switch path[i] {
|
||||
case '.':
|
||||
i++
|
||||
if i < len(path) && path[i] == '.' {
|
||||
// Recursive descent
|
||||
i++
|
||||
key, nextPos := readKey(path, i)
|
||||
steps = append(steps, JSONStep{Type: RecursiveDescentStep, Key: key})
|
||||
i = nextPos
|
||||
} else {
|
||||
// Child step or wildcard
|
||||
key, nextPos := readKey(path, i)
|
||||
if key == "*" {
|
||||
steps = append(steps, JSONStep{Type: WildcardStep})
|
||||
} else {
|
||||
steps = append(steps, JSONStep{Type: ChildStep, Key: key})
|
||||
}
|
||||
i = nextPos
|
||||
}
|
||||
|
||||
case '[':
|
||||
// Index step
|
||||
i++
|
||||
indexStr, nextPos := readIndex(path, i)
|
||||
if indexStr == "*" {
|
||||
steps = append(steps, JSONStep{Type: IndexStep, Index: -1})
|
||||
} else {
|
||||
index, err := strconv.Atoi(indexStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid index: %s", indexStr)
|
||||
}
|
||||
steps = append(steps, JSONStep{Type: IndexStep, Index: index})
|
||||
}
|
||||
i = nextPos + 1 // Skip closing ]
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected character: %c", path[i])
|
||||
}
|
||||
}
|
||||
|
||||
return steps, nil
|
||||
}
|
||||
|
||||
func readKey(path string, start int) (string, int) {
|
||||
i := start
|
||||
for ; i < len(path); i++ {
|
||||
if path[i] == '.' || path[i] == '[' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return path[start:i], i
|
||||
}
|
||||
|
||||
func readIndex(path string, start int) (string, int) {
|
||||
i := start
|
||||
for ; i < len(path); i++ {
|
||||
if path[i] == ']' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return path[start:i], i
|
||||
}
|
||||
|
||||
func EvaluateJSONPath(data interface{}, steps []JSONStep) []interface{} {
|
||||
current := []interface{}{data}
|
||||
|
||||
for _, step := range steps {
|
||||
var next []interface{}
|
||||
for _, node := range current {
|
||||
next = append(next, evalStep(node, step)...)
|
||||
}
|
||||
current = next
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
func evalStep(node interface{}, step JSONStep) []interface{} {
|
||||
switch step.Type {
|
||||
case ChildStep:
|
||||
return evalChild(node, step.Key)
|
||||
case RecursiveDescentStep:
|
||||
return evalRecursiveDescent(node, step.Key)
|
||||
case WildcardStep:
|
||||
return evalWildcard(node)
|
||||
case IndexStep:
|
||||
return evalIndex(node, step.Index)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func evalChild(node interface{}, key string) []interface{} {
|
||||
if m, ok := node.(map[string]interface{}); ok {
|
||||
if val, exists := m[key]; exists {
|
||||
return []interface{}{val}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func evalRecursiveDescent(node interface{}, targetKey string) []interface{} {
|
||||
results := []interface{}{}
|
||||
queue := []interface{}{node}
|
||||
|
||||
for len(queue) > 0 {
|
||||
current := queue[0]
|
||||
queue = queue[1:]
|
||||
|
||||
if m, ok := current.(map[string]interface{}); ok {
|
||||
// Check if current level has target key
|
||||
if val, exists := m[targetKey]; exists {
|
||||
results = append(results, val)
|
||||
}
|
||||
// Add all children to queue
|
||||
for _, v := range m {
|
||||
queue = append(queue, v)
|
||||
}
|
||||
} else if arr, ok := current.([]interface{}); ok {
|
||||
queue = append(queue, arr...)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func evalWildcard(node interface{}) []interface{} {
|
||||
if m, ok := node.(map[string]interface{}); ok {
|
||||
results := make([]interface{}, 0, len(m))
|
||||
for _, v := range m {
|
||||
results = append(results, v)
|
||||
}
|
||||
return results
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func evalIndex(node interface{}, index int) []interface{} {
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
if index == -1 { // Wildcard [*]
|
||||
return arr
|
||||
}
|
||||
if index >= 0 && index < len(arr) {
|
||||
return []interface{}{arr[index]}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user