package node import ( "encoding/json" "fmt" "io" "os" "reflect" "strings" ) type Node struct { name string data any parent *Node children []*Node } func NewNode() *Node { return &Node{} } func NewNodeWithName(name string) *Node { return &Node{name: name} } func NewNodeWithData(data any) *Node { return &Node{data: data} } func NewNodeWithNameAndData(name string, data any) *Node { return &Node{name: name, data: data} } func NewNodeWithDataAndName(data any, name string) *Node { return &Node{name: name, data: data} } func NewNodeFromJSON(data []byte) (*Node, error) { n := NewNode() err := json.Unmarshal(data, &n) if err != nil { return nil, err } return n, nil } func NewNodeFromFile(name string) (*Node, error) { pay, err := os.ReadFile(name) if err != nil { return nil, err } return NewNodeFromJSON(pay) } func NewNodeFromStream(r io.Reader) (*Node, error) { doc := json.NewDecoder(r) root := NewNode() work := root var ( depth int = 0 maplike bool = false lastToken string ) for { t, err := doc.Token() if err == io.EOF { break } if err != nil { return nil, err } delim, ok := t.(json.Delim) if ok { if rune(delim) == '{' || rune(delim) == '[' { work = work.NewKid() depth++ if rune(delim) == '{' { maplike = true } else { maplike = false } } else if rune(delim) == '}' || rune(delim) == ']' { if work.Parent() != nil { depth-- work = work.Parent() maplike = work.IsMapLike() } } } else { text := fmt.Sprintf("%v", t) if lastToken == "" { lastToken = text } else { lastToken = "" } fmt.Printf("%s%s", strings.Repeat(" ", depth), text) if doc.More() { fmt.Printf(" (more)") } fmt.Printf("\n") if work.Name() == "" && maplike { work.Name(text) } else if maplike { if !doc.More() { work.Data(t.(any)) } else { work.NewKidWithData(t.(any)) } } else if !maplike { work.NewKidWithData(t.(any)) } } } return root, nil } func (n *Node) Name(name ...string) string { if len(name) != 0 { n.name = name[0] } return n.name } func (n *Node) Data(data ...any) any { if len(data) != 0 { n.data = data[0] } return n.data } func (n *Node) Len() int { return len(n.children) } func (n *Node) Parent(parent ...*Node) *Node { if len(parent) != 0 { n.parent = parent[0] } return n.parent } func (n *Node) Root() *Node { if n.Parent() == nil { return n } at := n.Parent() for at.Parent() != nil { at = at.Parent() } return at } func (n *Node) Depth() int { if n.Parent() == nil { return 0 } var ( depth int = 1 at *Node = n.Parent() ) for at.Parent() != nil { depth++ at = at.Parent() } return depth } func (n *Node) Kids() []*Node { return n.children } func (n *Node) AddKid(kid *Node) bool { for _, k := range n.children { if k == kid { return false } } kid.Parent(n) n.children = append(n.children, kid) return true } func (n *Node) NewKid() *Node { k := &Node{} n.AddKid(k) return k } func (n *Node) NewKidWithName(name string) *Node { k := n.NewKid() k.Name(name) return k } func (n *Node) NewKidWithData(data any) *Node { k := n.NewKid() k.Data(data) return k } func (n *Node) NewKidWithNameAndData(name string, data any) *Node { k := n.NewKid() k.Name(name) k.Data(data) return k } func (n *Node) NewKidWithDataAndName(data any, name string) *Node { k := n.NewKid() k.Name(name) k.Data(data) return k } func (n *Node) Index() int { if n.Parent() == nil { // There is no parent, so it's not known return -1 } // Iterate thru the children of the parent to find ourselves for i, kid := range n.Parent().Kids() { if kid == n { return i } } // Ok we for some reason are not in that list, so it's not known return -1 } func (n *Node) Kid(index int) *Node { if n == nil || index > n.Len() || index < 0 { return nil } return n.children[index] } func (n *Node) KidByName(name string, recurse ...bool) *Node { for _, kid := range n.children { if kid.name == name { return kid } } if len(recurse) != 0 { if recurse[0] { for _, kid := range n.children { r := kid.KidByName(name, recurse...) if r != nil { return r } } } } return nil } func (n *Node) RemoveKid(index int) bool { if index > n.Len() || index < 0 { return false } n.children[index].Parent(nil) _, ok := arrayDelete(&n.children, index) return ok } func (n *Node) RemoveAllKids() { for _, kid := range n.children { kid.Parent(nil) } n.children = []*Node{} } func (n *Node) Detach() bool { if n.Parent() != nil { return false } index := n.Index() if index == -1 { return false } return n.Parent().RemoveKid(index) } func (n *Node) Destroy() { if n.Parent() != nil { n.Detach() } n.Data(nil) n.Name("") for _, kid := range n.children { kid.Destroy() } } // Checks if the children of this node are better represented as a map or an array // // This is determined by: // // * Totaling number of children without names // // * Comparing the percent of children without names to total children (if less than 50% use a map, if more than 50% use an array) func (n *Node) IsMapLike() bool { if n.Len() == 0 { return false } var ( total int = n.Len() // number of children noname int = 0 // number of children with no name ) for _, kid := range n.Kids() { if len(kid.Name()) == 0 { // When it has no name it's not map-like noname += 1 } } // Form a percent of no name children and compare it to less than 50% return int((float32(noname)/float32(total))*100.0) <= 50 } func (n *Node) from(A any) { switch reflect.TypeOf(A).Kind() { case reflect.Map: // Ok, key, value // key == name, value if not a map, array, slice is data m := A.(map[string]any) for k, v := range m { switch reflect.TypeOf(v).Kind() { case reflect.Map, reflect.Array, reflect.Slice: if n.Depth() != 0 || len(m) > 1 { // New kid, with name, and recurse kid := n.NewKidWithName(k) kid.from(v) } else { // This is within the "root" node n.Name(k) n.from(v) } default: // New kid, with name, and data n.NewKidWithNameAndData(k, v) } } case reflect.Array, reflect.Slice: // Ok, values // no name (use index), value if not a map, array, slice is data a := A.([]any) for idx, v := range a { switch reflect.TypeOf(v).Kind() { case reflect.Map, reflect.Array, reflect.Slice: if n.Depth() != 0 || len(a) > 1 { kid := n.NewKidWithName(fmt.Sprintf("%d", idx)) kid.from(v) } else { n.Name(fmt.Sprintf("%d", idx)) n.from(v) } default: n.NewKidWithNameAndData(fmt.Sprintf("%d", idx), v) } } default: // Ok it's just a single value (most likely) n.Data(A) } } func (n *Node) ToMap() any { if len(n.Name()) != 0 && n.Len() != 0 { // Name + Children if n.IsMapLike() { // Map m := map[string]any{} for i, kid := range n.children { if len(kid.Name()) != 0 { m[kid.Name()] = kid.ToMap() } else { m[fmt.Sprint(i)] = kid.ToMap() } } if n.Depth() != 0 { return m } else { return map[string]any{ n.Name(): m, } } } else { // Array a := []any{} for _, kid := range n.children { a = append(a, kid.ToMap()) } if n.Depth() != 0 { return a } else { return map[string]any{ n.Name(): a, } } } } else if len(n.Name()) == 0 && n.Len() != 0 { // No Name + Children if n.IsMapLike() { // Map m := map[string]any{} for i, kid := range n.children { if len(kid.Name()) != 0 { m[kid.Name()] = kid.ToMap() } else { m[fmt.Sprint(i)] = kid.ToMap() } } return m } else { // Array a := []any{} for _, kid := range n.children { a = append(a, kid.ToMap()) } if n.Depth() != 0 { return a } else { return map[string]any{ fmt.Sprint(n.Index()): a, } } } } else if n.Data() != nil { if n.Depth() != 0 || len(n.Name()) == 0 { return n.Data() } else { return map[string]any{ n.Name(): n.Data(), } } } return nil } // Marshals the results from ToMap func (n *Node) MarshalJSON() ([]byte, error) { return json.Marshal(n.ToMap()) } // Unmarshal function func (n *Node) UnmarshalJSON(pay []byte) error { var m map[string]any err := json.Unmarshal(pay, &m) if err != nil { return err } n.Destroy() // In this case, the struct isn't in a invalid state, it's just a reset n.from(m) return nil }