forked from clbanning/x2j
-
Notifications
You must be signed in to change notification settings - Fork 0
/
x2j_valuesFrom.go
125 lines (115 loc) · 3.84 KB
/
x2j_valuesFrom.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright 2012-2013 Charles Banning. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// x2j_valuesFrom.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
package x2j
import (
"strings"
)
// ------------------- sweep up everything for some point in the node tree ---------------------
// ValuesFromTagPath - deliver all values for a path node from a XML doc
// If there are no values for the path 'nil' is returned.
// A return value of (nil, nil) means that there were no values and no errors parsing the doc.
// 'doc' is the XML document
// 'path' is a dot-separated path of tag nodes
// 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path
// If a node is '*', then everything beyond is scanned for values.
// E.g., "doc.books' might return a single value 'book' of type []interface{}, but
// "doc.books.*" could return all the 'book' entries as []map[string]interface{}.
// "doc.books.*.author" might return all the 'author' tag values as []string - or
// "doc.books.*.author.lastname" might be required, depending on he schema.
func ValuesFromTagPath(doc, path string, getAttrs ...bool) ([]interface{}, error) {
var a bool
if len(getAttrs) == 1 {
a = getAttrs[0]
}
m, err := DocToMap(doc)
if err != nil {
return nil, err
}
v := ValuesFromKeyPath(m, path, a)
return v, nil
}
// ValuesFromKeyPath - deliver all values for a path node from a map[string]interface{}
// If there are no values for the path 'nil' is returned.
// 'm' is the map to be walked
// 'path' is a dot-separated path of key values
// 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path
// If a node is '*', then everything beyond is walked.
// E.g., see ValuesFromTagPath documentation.
func ValuesFromKeyPath(m map[string]interface{}, path string, getAttrs ...bool) []interface{} {
var a bool
if len(getAttrs) == 1 {
a = getAttrs[0]
}
keys := strings.Split(path, ".")
ret := make([]interface{}, 0)
valuesFromKeyPath(&ret, m, keys, a)
if len(ret) == 0 {
return nil
}
return ret
}
func valuesFromKeyPath(ret *[]interface{}, m interface{}, keys []string, getAttrs bool) {
lenKeys := len(keys)
// load 'm' values into 'ret'
// expand any lists
if lenKeys == 0 {
switch m.(type) {
case map[string]interface{}:
*ret = append(*ret, m)
case []interface{}:
for _, v := range m.([]interface{}) {
*ret = append(*ret, v)
}
default:
*ret = append(*ret, m)
}
return
}
// key of interest
key := keys[0]
switch key {
case "*": // wildcard - scan all values
switch m.(type) {
case map[string]interface{}:
for k, v := range m.(map[string]interface{}) {
if string(k[:1]) == "-" && !getAttrs { // skip attributes?
continue
}
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
}
case []interface{}:
for _, v := range m.([]interface{}) {
switch v.(type) {
// flatten out a list of maps - keys are processed
case map[string]interface{}:
for kk, vv := range v.(map[string]interface{}) {
if string(kk[:1]) == "-" && !getAttrs { // skip attributes?
continue
}
valuesFromKeyPath(ret, vv, keys[1:], getAttrs)
}
default:
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
}
}
}
default: // key - must be map[string]interface{}
switch m.(type) {
case map[string]interface{}:
if v, ok := m.(map[string]interface{})[key]; ok {
valuesFromKeyPath(ret, v, keys[1:], getAttrs)
}
case []interface{}: // may be buried in list
for _, v := range m.([]interface{}) {
switch v.(type) {
case map[string]interface{}:
if vv, ok := v.(map[string]interface{})[key]; ok {
valuesFromKeyPath(ret, vv, keys[1:], getAttrs)
}
}
}
}
}
}