416 lines
13 KiB
Go
416 lines
13 KiB
Go
package hjson
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
type Comments struct {
|
|
// Comment/whitespace on line(s) before the value, and before the value on
|
|
// the same line. If not empty, is expected to end with a line feed +
|
|
// indentation for the value.
|
|
Before string
|
|
// Comment/whitespace between the key and this value, if this value is an
|
|
// element in a map/object.
|
|
Key string
|
|
// Comment/whitespace after (but still on the same line as) the leading
|
|
// bracket ({ or [) for this value, if this value is a slice/array or
|
|
// map/object. Is not expected to contain any line feed.
|
|
InsideFirst string
|
|
// Comment/whitespace from the beginning of the first line after all child
|
|
// values belonging to this value, until the closing bracket (} or ]), if
|
|
// this value is a slice/array or map/object. If not empty, is expected to
|
|
// end with a line feed + indentation for the closing bracket.
|
|
InsideLast string
|
|
// Comment/whitespace after (but still on the same line as) the value. Is not
|
|
// expected to contain any line feed. hjson.Unmarshal() will try to assign
|
|
// comments/whitespace from lines between values to `Before` on the value
|
|
// after those lines, or to `InsideLast` on the slice/map if the lines
|
|
// containing comments appear after the last element inside a slice/map.
|
|
After string
|
|
}
|
|
|
|
// Node must be used as destination for Unmarshal() or UnmarshalWithOptions()
|
|
// whenever comments should be read from the input. The struct is simply a
|
|
// wrapper for the actual values and a helper struct containing any comments.
|
|
// The Value in the destination Node will be overwritten in the call to
|
|
// Unmarshal() or UnmarshalWithOptions(), i.e. node trees are not merged.
|
|
// After the unmarshal, Node.Value will contain any of these types:
|
|
//
|
|
// nil (no type)
|
|
// float64 (if UseJSONNumber == false)
|
|
// json.Number (if UseJSONNumber == true)
|
|
// string
|
|
// bool
|
|
// []interface{}
|
|
// *hjson.OrderedMap
|
|
//
|
|
// All elements in an []interface{} or *hjson.OrderedMap will be of the type
|
|
// *hjson.Node, so that they can contain comments.
|
|
//
|
|
// This example shows unmarshalling input with comments, changing the value on
|
|
// a single key (the input is assumed to have an object/map as root) and then
|
|
// marshalling the node tree again, including comments and with preserved key
|
|
// order in the object/map.
|
|
//
|
|
// var node hjson.Node
|
|
// err := hjson.Unmarshal(input, &node)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// _, err = node.SetKey("setting1", 3)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// output, err := hjson.Marshal(node)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
type Node struct {
|
|
Value interface{}
|
|
Cm Comments
|
|
}
|
|
|
|
// Len returns the length of the value wrapped by this Node, if the value is of
|
|
// type *hjson.OrderedMap, []interface{} or string. Otherwise 0 is returned.
|
|
func (c *Node) Len() int {
|
|
if c == nil {
|
|
return 0
|
|
}
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
return cont.Len()
|
|
case []interface{}:
|
|
return len(cont)
|
|
case string:
|
|
return len(cont)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// AtIndex returns the key (if any) and value (unwrapped from its Node) found
|
|
// at the specified index, if this Node contains a value of type
|
|
// *hjson.OrderedMap or []interface{}. Returns an error for unexpected types.
|
|
// Panics if index < 0 or index >= Len().
|
|
func (c *Node) AtIndex(index int) (string, interface{}, error) {
|
|
if c == nil {
|
|
return "", nil, fmt.Errorf("Node is nil")
|
|
}
|
|
var key string
|
|
var elem interface{}
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
key = cont.Keys[index]
|
|
elem = cont.Map[key]
|
|
case []interface{}:
|
|
elem = cont[index]
|
|
default:
|
|
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
node, ok := elem.(*Node)
|
|
if !ok {
|
|
return "", nil, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
|
|
}
|
|
return key, node.Value, nil
|
|
}
|
|
|
|
// AtKey returns the value (unwrapped from its Node) found for the specified
|
|
// key, if this Node contains a value of type *hjson.OrderedMap. An error is
|
|
// returned for unexpected types. The second returned value is true if the key
|
|
// was found, false otherwise.
|
|
func (c *Node) AtKey(key string) (interface{}, bool, error) {
|
|
if c == nil {
|
|
return nil, false, nil
|
|
}
|
|
om, ok := c.Value.(*OrderedMap)
|
|
if !ok {
|
|
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
elem, ok := om.Map[key]
|
|
if !ok {
|
|
return nil, false, nil
|
|
}
|
|
node, ok := elem.(*Node)
|
|
if !ok {
|
|
return nil, false, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
|
|
}
|
|
return node.Value, true, nil
|
|
}
|
|
|
|
// Append adds the input value to the end of the []interface{} wrapped by this
|
|
// Node. If this Node contains nil without a type, an empty []interface{} is
|
|
// first created. If this Node contains a value of any other type, an error is
|
|
// returned.
|
|
func (c *Node) Append(value interface{}) error {
|
|
if c == nil {
|
|
return fmt.Errorf("Node is nil")
|
|
}
|
|
var arr []interface{}
|
|
if c.Value == nil {
|
|
arr = []interface{}{}
|
|
} else {
|
|
var ok bool
|
|
arr, ok = c.Value.([]interface{})
|
|
if !ok {
|
|
return fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
}
|
|
c.Value = append(arr, &Node{Value: value})
|
|
return nil
|
|
}
|
|
|
|
// Insert inserts a new key/value pair at the specified index, if this Node
|
|
// contains a value of type *hjson.OrderedMap or []interface{}. Returns an error
|
|
// for unexpected types. Panics if index < 0 or index > c.Len(). If the key
|
|
// already exists in the OrderedMap, the new value is set but the position of
|
|
// the key is not changed. Otherwise the value to insert is wrapped in a new
|
|
// Node. If this Node contains []interface{}, the key is ignored. Returns the
|
|
// old value and true if the key already exists in the/ OrderedMap, nil and
|
|
// false otherwise.
|
|
func (c *Node) Insert(index int, key string, value interface{}) (interface{}, bool, error) {
|
|
if c == nil {
|
|
return nil, false, fmt.Errorf("Node is nil")
|
|
}
|
|
var oldVal interface{}
|
|
var found bool
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
oldVal, found := cont.Map[key]
|
|
if found {
|
|
if node, ok := oldVal.(*Node); ok {
|
|
oldVal = node.Value
|
|
node.Value = value
|
|
} else {
|
|
cont.Map[key] = &Node{Value: value}
|
|
}
|
|
} else {
|
|
oldVal, found = cont.Insert(index, key, &Node{Value: value})
|
|
}
|
|
case []interface{}:
|
|
value = &Node{Value: value}
|
|
if index == len(cont) {
|
|
c.Value = append(cont, value)
|
|
} else {
|
|
cont = append(cont[:index+1], cont[index:]...)
|
|
cont[index] = value
|
|
c.Value = cont
|
|
}
|
|
default:
|
|
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
return oldVal, found, nil
|
|
}
|
|
|
|
// SetIndex assigns the specified value to the child Node found at the specified
|
|
// index, if this Node contains a value of type *hjson.OrderedMap or
|
|
// []interface{}. Returns an error for unexpected types. Returns the key (if
|
|
// any) and value previously found at the specified index. Panics if index < 0
|
|
// or index >= Len().
|
|
func (c *Node) SetIndex(index int, value interface{}) (string, interface{}, error) {
|
|
if c == nil {
|
|
return "", nil, fmt.Errorf("Node is nil")
|
|
}
|
|
var key string
|
|
var elem interface{}
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
key = cont.Keys[index]
|
|
elem = cont.Map[key]
|
|
case []interface{}:
|
|
elem = cont[index]
|
|
default:
|
|
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
var oldVal interface{}
|
|
node, ok := elem.(*Node)
|
|
if ok {
|
|
oldVal = node.Value
|
|
node.Value = value
|
|
} else {
|
|
oldVal = elem
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
cont.Map[key] = &Node{Value: value}
|
|
case []interface{}:
|
|
cont[index] = &Node{Value: value}
|
|
}
|
|
}
|
|
return key, oldVal, nil
|
|
}
|
|
|
|
// SetKey assigns the specified value to the child Node identified by the
|
|
// specified key, if this Node contains a value of the type *hjson.OrderedMap.
|
|
// If this Node contains nil without a type, an empty *hjson.OrderedMap is
|
|
// first created. If this Node contains a value of any other type an error is
|
|
// returned. If the key cannot be found in the OrderedMap, a new Node is
|
|
// created, wrapping the specified value, and appended to the end of the
|
|
// OrderedMap. Returns the old value and true if the key already existed in
|
|
// the OrderedMap, nil and false otherwise.
|
|
func (c *Node) SetKey(key string, value interface{}) (interface{}, bool, error) {
|
|
if c == nil {
|
|
return nil, false, fmt.Errorf("Node is nil")
|
|
}
|
|
var om *OrderedMap
|
|
if c.Value == nil {
|
|
om = NewOrderedMap()
|
|
c.Value = om
|
|
} else {
|
|
var ok bool
|
|
om, ok = c.Value.(*OrderedMap)
|
|
if !ok {
|
|
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
}
|
|
var oldVal interface{}
|
|
elem, ok := om.Map[key]
|
|
if ok {
|
|
var node *Node
|
|
node, ok = elem.(*Node)
|
|
if ok {
|
|
oldVal = node.Value
|
|
node.Value = value
|
|
}
|
|
}
|
|
foundKey := true
|
|
if !ok {
|
|
oldVal, foundKey = om.Set(key, &Node{Value: value})
|
|
}
|
|
return oldVal, foundKey, nil
|
|
}
|
|
|
|
// DeleteIndex deletes the value or key/value pair found at the specified index,
|
|
// if this Node contains a value of type *hjson.OrderedMap or []interface{}.
|
|
// Returns an error for unexpected types. Panics if index < 0 or
|
|
// index >= c.Len(). Returns the deleted key (if any) and value.
|
|
func (c *Node) DeleteIndex(index int) (string, interface{}, error) {
|
|
if c == nil {
|
|
return "", nil, fmt.Errorf("Node is nil")
|
|
}
|
|
var key string
|
|
var value interface{}
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
key, value = cont.DeleteIndex(index)
|
|
case []interface{}:
|
|
value = cont[index]
|
|
cont = append(cont[:index], cont[index+1:]...)
|
|
c.Value = cont
|
|
default:
|
|
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
if node, ok := value.(*Node); ok {
|
|
value = node.Value
|
|
}
|
|
return key, value, nil
|
|
}
|
|
|
|
// DeleteKey deletes the key/value pair with the specified key, if found and if
|
|
// this Node contains a value of type *hjson.OrderedMap. Returns an error for
|
|
// unexpected types. Returns the deleted value and true if the key was found,
|
|
// nil and false otherwise.
|
|
func (c *Node) DeleteKey(key string) (interface{}, bool, error) {
|
|
if c == nil {
|
|
return nil, false, fmt.Errorf("Node is nil")
|
|
}
|
|
if om, ok := c.Value.(*OrderedMap); ok {
|
|
oldValue, found := om.DeleteKey(key)
|
|
if node, ok := oldValue.(*Node); ok {
|
|
oldValue = node.Value
|
|
}
|
|
return oldValue, found, nil
|
|
}
|
|
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
|
}
|
|
|
|
// NI is an acronym formed from "get Node pointer by Index". Returns the *Node
|
|
// element found at the specified index, if this Node contains a value of type
|
|
// *hjson.OrderedMap or []interface{}. Returns nil otherwise. Panics if
|
|
// index < 0 or index >= Len(). Does not create or alter any value.
|
|
func (c *Node) NI(index int) *Node {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
var elem interface{}
|
|
switch cont := c.Value.(type) {
|
|
case *OrderedMap:
|
|
elem = cont.AtIndex(index)
|
|
case []interface{}:
|
|
elem = cont[index]
|
|
default:
|
|
return nil
|
|
}
|
|
if node, ok := elem.(*Node); ok {
|
|
return node
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NK is an acronym formed from "get Node pointer by Key". Returns the *Node
|
|
// element found for the specified key, if this Node contains a value of type
|
|
// *hjson.OrderedMap. Returns nil otherwise. Does not create or alter anything.
|
|
func (c *Node) NK(key string) *Node {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
om, ok := c.Value.(*OrderedMap)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if elem, ok := om.Map[key]; ok {
|
|
if node, ok := elem.(*Node); ok {
|
|
return node
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NKC is an acronym formed from "get Node pointer by Key, Create if not found".
|
|
// Returns the *Node element found for the specified key, if this Node contains
|
|
// a value of type *hjson.OrderedMap. If this Node contains nil without a type,
|
|
// an empty *hjson.OrderedMap is first created. If this Node contains a value of
|
|
// any other type or if the element idendified by the specified key is not of
|
|
// type *Node, an error is returned. If the key cannot be found in the
|
|
// OrderedMap, a new Node is created for the specified key. Example usage:
|
|
//
|
|
// var node hjson.Node
|
|
// node.NKC("rootKey1").NKC("subKey1").SetKey("valKey1", "my value")
|
|
func (c *Node) NKC(key string) *Node {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
var om *OrderedMap
|
|
if c.Value == nil {
|
|
om = NewOrderedMap()
|
|
c.Value = om
|
|
} else {
|
|
var ok bool
|
|
om, ok = c.Value.(*OrderedMap)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
}
|
|
if elem, ok := om.Map[key]; ok {
|
|
if node, ok := elem.(*Node); ok {
|
|
return node
|
|
}
|
|
} else {
|
|
node := &Node{}
|
|
om.Set(key, node)
|
|
return node
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
|
|
// hjson.Node trees to be used as input for json.Marshal().
|
|
func (c Node) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(c.Value)
|
|
}
|
|
|
|
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
|
|
// enabling hjson.Node to be used as destination for json.Unmarshal().
|
|
func (c *Node) UnmarshalJSON(b []byte) error {
|
|
return Unmarshal(b, c)
|
|
}
|