...

Source file src/github.com/clbanning/mxj/v2/x2j-wrapper/x2j_valuesFrom.go

Documentation: github.com/clbanning/mxj/v2/x2j-wrapper

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

View as plain text