1 // Copyright 2012-2018 Charles Banning. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file 4 5 // x2j_valuesAt.go: Extract values from an arbitrary XML doc that are at same level as "key". 6 // Tag path can include wildcard characters. 7 8 package x2j 9 10 import ( 11 "strings" 12 13 "github.com/clbanning/mxj" 14 ) 15 16 // ------------------- sweep up everything for some point in the node tree --------------------- 17 18 // ValuesAtTagPath - deliver all values at the same level of the document as the specified key. 19 // See ValuesAtKeyPath(). 20 // If there are no values for the path 'nil' is returned. 21 // A return value of (nil, nil) means that there were no values and no errors parsing the doc. 22 // 'doc' is the XML document 23 // 'path' is a dot-separated path of tag nodes 24 // 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path 25 // If a node is '*', then everything beyond is scanned for values. 26 // E.g., "doc.books' might return a single value 'book' of type []interface{}, but 27 // "doc.books.*" could return all the 'book' entries as []map[string]interface{}. 28 // "doc.books.*.author" might return all the 'author' tag values as []string - or 29 // "doc.books.*.author.lastname" might be required, depending on he schema. 30 func ValuesAtTagPath(doc, path string, getAttrs ...bool) ([]interface{}, error) { 31 var a bool 32 if len(getAttrs) == 1 { 33 a = getAttrs[0] 34 } 35 m, err := mxj.NewMapXml([]byte(doc)) 36 if err != nil { 37 return nil, err 38 } 39 40 v := ValuesAtKeyPath(m, path, a) 41 return v, nil 42 } 43 44 // ValuesAtKeyPath - deliver all values at the same depth in a map[string]interface{} value 45 // If v := ValuesAtKeyPath(m,"x.y.z") 46 // then there exists a _,vv := range v 47 // such that v.(map[string]interface{})[z] == ValuesFromKeyPath(m,"x.y.z") 48 // If there are no values for the path 'nil' is returned. 49 // 'm' is the map to be walked 50 // 'path' is a dot-separated path of key values 51 // 'getAttrs' can be set 'true' to return attribute values for "*"-terminated path 52 // If a node is '*', then everything beyond is walked. 53 // E.g., see ValuesFromTagPath documentation. 54 func ValuesAtKeyPath(m map[string]interface{}, path string, getAttrs ...bool) []interface{} { 55 var a bool 56 if len(getAttrs) == 1 { 57 a = getAttrs[0] 58 } 59 keys := strings.Split(path, ".") 60 lenKeys := len(keys) 61 ret := make([]interface{}, 0) 62 if lenKeys > 1 { 63 // use function in x2j_valuesFrom.go 64 valuesFromKeyPath(&ret, m, keys[:lenKeys-1], a) 65 if len(ret) == 0 { 66 return nil 67 } 68 } else { 69 ret = append(ret,interface{}(m)) 70 } 71 72 // scan the value set and see if key occurs 73 key := keys[lenKeys-1] 74 // wildcard is special 75 if key == "*" { 76 return ret 77 } 78 for _, v := range ret { 79 switch v.(type) { 80 case map[string]interface{}: 81 if _, ok := v.(map[string]interface{})[key]; ok { 82 return ret 83 } 84 } 85 } 86 87 // no instance of key in penultimate value set 88 return nil 89 } 90 91