databricks-cli/libs/dyn/jsonloader/json.go

99 lines
2.6 KiB
Go
Raw Normal View History

package jsonloader
import (
2024-10-03 14:07:46 +00:00
"bytes"
"encoding/json"
2024-10-03 14:07:46 +00:00
"fmt"
"io"
"github.com/databricks/cli/libs/dyn"
)
2024-10-08 13:36:57 +00:00
func LoadJSON(data []byte, source string) (dyn.Value, error) {
offset := BuildLineOffsets(data)
offset.SetSource(source)
2024-10-03 14:07:46 +00:00
reader := bytes.NewReader(data)
decoder := json.NewDecoder(reader)
// Start decoding from the top-level value
2024-10-08 13:36:57 +00:00
value, err := decodeValue(decoder, &offset)
2024-10-03 14:07:46 +00:00
if err != nil {
if err == io.EOF {
err = fmt.Errorf("unexpected end of JSON input")
}
2024-10-08 13:36:57 +00:00
return dyn.InvalidValue, fmt.Errorf("error decoding JSON at %s: %v", value.Location(), err)
2024-10-03 14:07:46 +00:00
}
return value, nil
}
2024-10-08 13:36:57 +00:00
func decodeValue(decoder *json.Decoder, o *Offset) (dyn.Value, error) {
2024-10-03 14:07:46 +00:00
// Read the next JSON token
token, err := decoder.Token()
if err != nil {
return dyn.InvalidValue, err
}
2024-10-03 14:07:46 +00:00
// Get the current byte offset
offset := decoder.InputOffset()
2024-10-08 13:36:57 +00:00
location := o.GetPosition(offset)
2024-10-03 14:07:46 +00:00
switch tok := token.(type) {
case json.Delim:
if tok == '{' {
// Decode JSON object
2024-10-08 13:36:57 +00:00
obj := dyn.NewMapping()
2024-10-03 14:07:46 +00:00
for decoder.More() {
// Decode the key
keyToken, err := decoder.Token()
if err != nil {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), err
2024-10-03 14:07:46 +00:00
}
key, ok := keyToken.(string)
if !ok {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), fmt.Errorf("expected string for object key")
2024-10-03 14:07:46 +00:00
}
2024-10-08 13:36:57 +00:00
keyVal := dyn.NewValue(key, []dyn.Location{o.GetPosition(decoder.InputOffset())})
2024-10-03 14:07:46 +00:00
// Decode the value recursively
2024-10-08 13:36:57 +00:00
val, err := decodeValue(decoder, o)
2024-10-03 14:07:46 +00:00
if err != nil {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), err
2024-10-03 14:07:46 +00:00
}
2024-10-08 13:36:57 +00:00
obj.Set(keyVal, val)
2024-10-03 14:07:46 +00:00
}
// Consume the closing '}'
if _, err := decoder.Token(); err != nil {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), err
2024-10-03 14:07:46 +00:00
}
return dyn.NewValue(obj, []dyn.Location{location}), nil
} else if tok == '[' {
// Decode JSON array
var arr []dyn.Value
for decoder.More() {
2024-10-08 13:36:57 +00:00
val, err := decodeValue(decoder, o)
2024-10-03 14:07:46 +00:00
if err != nil {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), err
2024-10-03 14:07:46 +00:00
}
arr = append(arr, val)
}
// Consume the closing ']'
if _, err := decoder.Token(); err != nil {
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), err
2024-10-03 14:07:46 +00:00
}
return dyn.NewValue(arr, []dyn.Location{location}), nil
}
default:
// Primitive types: string, number, bool, or null
return dyn.NewValue(tok, []dyn.Location{location}), nil
}
2024-10-03 14:07:46 +00:00
2024-10-08 13:36:57 +00:00
return invalidValueWithLocation(decoder, o), fmt.Errorf("unexpected token: %v", token)
}
func invalidValueWithLocation(decoder *json.Decoder, o *Offset) dyn.Value {
location := o.GetPosition(decoder.InputOffset())
return dyn.InvalidValue.WithLocations([]dyn.Location{location})
}