mirror of
https://github.com/astaxie/beego.git
synced 2024-11-18 05:20:55 +00:00
126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
|
// 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)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|