1 package mxj
2 3 // leafnode.go - return leaf nodes with paths and values for the Map
4 // inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw
5 6 import (
7 "strconv"
8 "strings"
9 )
10 11 const (
12 NoAttributes = true // suppress LeafNode values that are attributes
13 )
14 15 // LeafNode - a terminal path value in a Map.
16 // For XML Map values it represents an attribute or simple element value - of type
17 // string unless Map was created using Cast flag. For JSON Map values it represents
18 // a string, numeric, boolean, or null value.
19 type LeafNode struct {
20 Path string // a dot-notation representation of the path with array subscripting
21 Value interface{} // the value at the path termination
22 }
23 24 // LeafNodes - returns an array of all LeafNode values for the Map.
25 // The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-')
26 // as well as the "#text" key for the associated simple element value.
27 //
28 // PrependAttrWithHypen(false) will result in attributes having .attr-name as
29 // terminal node in 'path' while the path for the element value, itself, will be
30 // the base path w/o "#text".
31 //
32 // LeafUseDotNotation(true) causes list members to be identified using ".N" syntax
33 // rather than "[N]" syntax.
34 func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
35 var a bool
36 if len(no_attr) == 1 {
37 a = no_attr[0]
38 }
39 40 l := make([]LeafNode, 0)
41 getLeafNodes("", "", map[string]interface{}(mv), &l, a)
42 return l
43 }
44 45 func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
46 // if stripping attributes, then also strip "#text" key
47 if !noattr || node != textK {
48 if path != "" && node[:1] != "[" {
49 path += "."
50 }
51 path += node
52 }
53 switch mv.(type) {
54 case map[string]interface{}:
55 for k, v := range mv.(map[string]interface{}) {
56 // if noattr && k[:1] == "-" {
57 if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
58 continue
59 }
60 getLeafNodes(path, k, v, l, noattr)
61 }
62 case []interface{}:
63 for i, v := range mv.([]interface{}) {
64 if useDotNotation {
65 getLeafNodes(path, strconv.Itoa(i), v, l, noattr)
66 } else {
67 getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr)
68 }
69 }
70 default:
71 // can't walk any further, so create leaf
72 n := LeafNode{path, mv}
73 *l = append(*l, n)
74 }
75 }
76 77 // LeafPaths - all paths that terminate in LeafNode values.
78 func (mv Map) LeafPaths(no_attr ...bool) []string {
79 ln := mv.LeafNodes()
80 ss := make([]string, len(ln))
81 for i := 0; i < len(ln); i++ {
82 ss[i] = ln[i].Path
83 }
84 return ss
85 }
86 87 // LeafValues - all terminal values in the Map.
88 func (mv Map) LeafValues(no_attr ...bool) []interface{} {
89 ln := mv.LeafNodes()
90 vv := make([]interface{}, len(ln))
91 for i := 0; i < len(ln); i++ {
92 vv[i] = ln[i].Value
93 }
94 return vv
95 }
96 97 // ====================== utilities ======================
98 99 // https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I
100 var useDotNotation bool
101 102 // LeafUseDotNotation sets a flag that list members in LeafNode paths
103 // should be identified using ".N" syntax rather than the default "[N]"
104 // syntax. Calling LeafUseDotNotation with no arguments toggles the
105 // flag on/off; otherwise, the argument sets the flag value 'true'/'false'.
106 func LeafUseDotNotation(b ...bool) {
107 if len(b) == 0 {
108 useDotNotation = !useDotNotation
109 return
110 }
111 useDotNotation = b[0]
112 }
113