leafnode.go 3.3 KB

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