123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- package mxj
- // leafnode.go - return leaf nodes with paths and values for the Map
- // inspired by: https://groups.google.com/forum/#!topic/golang-nuts/3JhuVKRuBbw
- import (
- "strconv"
- "strings"
- )
- const (
- NoAttributes = true // suppress LeafNode values that are attributes
- )
- // LeafNode - a terminal path value in a Map.
- // For XML Map values it represents an attribute or simple element value - of type
- // string unless Map was created using Cast flag. For JSON Map values it represents
- // a string, numeric, boolean, or null value.
- type LeafNode struct {
- Path string // a dot-notation representation of the path with array subscripting
- Value interface{} // the value at the path termination
- }
- // LeafNodes - returns an array of all LeafNode values for the Map.
- // The option no_attr argument suppresses attribute values (keys with prepended hyphen, '-')
- // as well as the "#text" key for the associated simple element value.
- //
- // PrependAttrWithHypen(false) will result in attributes having .attr-name as
- // terminal node in 'path' while the path for the element value, itself, will be
- // the base path w/o "#text".
- //
- // LeafUseDotNotation(true) causes list members to be identified using ".N" syntax
- // rather than "[N]" syntax.
- func (mv Map) LeafNodes(no_attr ...bool) []LeafNode {
- var a bool
- if len(no_attr) == 1 {
- a = no_attr[0]
- }
- l := make([]LeafNode, 0)
- getLeafNodes("", "", map[string]interface{}(mv), &l, a)
- return l
- }
- func getLeafNodes(path, node string, mv interface{}, l *[]LeafNode, noattr bool) {
- // if stripping attributes, then also strip "#text" key
- if !noattr || node != "#text" {
- if path != "" && node[:1] != "[" {
- path += "."
- }
- path += node
- }
- switch mv.(type) {
- case map[string]interface{}:
- for k, v := range mv.(map[string]interface{}) {
- // if noattr && k[:1] == "-" {
- if noattr && len(attrPrefix) > 0 && strings.Index(k, attrPrefix) == 0 {
- continue
- }
- getLeafNodes(path, k, v, l, noattr)
- }
- case []interface{}:
- for i, v := range mv.([]interface{}) {
- if useDotNotation {
- getLeafNodes(path, strconv.Itoa(i), v, l, noattr)
- } else {
- getLeafNodes(path, "["+strconv.Itoa(i)+"]", v, l, noattr)
- }
- }
- default:
- // can't walk any further, so create leaf
- n := LeafNode{path, mv}
- *l = append(*l, n)
- }
- }
- // LeafPaths - all paths that terminate in LeafNode values.
- func (mv Map) LeafPaths(no_attr ...bool) []string {
- ln := mv.LeafNodes()
- ss := make([]string, len(ln))
- for i := 0; i < len(ln); i++ {
- ss[i] = ln[i].Path
- }
- return ss
- }
- // LeafValues - all terminal values in the Map.
- func (mv Map) LeafValues(no_attr ...bool) []interface{} {
- ln := mv.LeafNodes()
- vv := make([]interface{}, len(ln))
- for i := 0; i < len(ln); i++ {
- vv[i] = ln[i].Value
- }
- return vv
- }
- // ====================== utilities ======================
- // https://groups.google.com/forum/#!topic/golang-nuts/pj0C5IrZk4I
- var useDotNotation bool
- // LeafUseDotNotation sets a flag that list members in LeafNode paths
- // should be identified using ".N" syntax rather than the default "[N]"
- // syntax. Calling LeafUseDotNotation with no arguments toggles the
- // flag on/off; otherwise, the argument sets the flag value 'true'/'false'.
- func LeafUseDotNotation(b ...bool) {
- if len(b) == 0 {
- useDotNotation = !useDotNotation
- return
- }
- useDotNotation = b[0]
- }
|