...

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

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

     1  // x2j_findPath - utility functions to retrieve path to node in dot-notation
     2  // Copyright 2012-2018 Charles Banning. All rights reserved.
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file
     5  
     6  package x2j
     7  
     8  import (
     9  	"strings"
    10  
    11  	"github.com/clbanning/mxj"
    12  )
    13  
    14  //----------------------------- find all paths to a key --------------------------------
    15  // Want eventually to extract shortest path and call GetValuesAtKeyPath()
    16  // This will get all the possible paths.  These can be scanned for len(path) and sequence.
    17  
    18  // Get all paths through the doc (in dot-notation) that terminate with the specified tag.
    19  // Results can be used with ValuesAtTagPath() and ValuesFromTagPath().
    20  func PathsForTag(doc string, key string) ([]string, error) {
    21  	m, err := mxj.NewMapXml([]byte(doc))
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  
    26  	ss := PathsForKey(m, key)
    27  	return ss, nil
    28  }
    29  
    30  // Extract the shortest path from all possible paths - from PathsForTag().
    31  // Paths are strings using dot-notation.
    32  func PathForTagShortest(doc string, key string) (string, error) {
    33  	m, err := mxj.NewMapXml([]byte(doc))
    34  	if err != nil {
    35  		return "", err
    36  	}
    37  
    38  	s := PathForKeyShortest(m, key)
    39  	return s, nil
    40  }
    41  
    42  // Get all paths through the doc (in dot-notation) that terminate with the specified tag.
    43  // Results can be used with ValuesAtTagPath() and ValuesFromTagPath().
    44  func BytePathsForTag(doc []byte, key string) ([]string, error) {
    45  	m, err := mxj.NewMapXml(doc)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	ss := PathsForKey(m, key)
    51  	return ss, nil
    52  }
    53  
    54  // Extract the shortest path from all possible paths - from PathsForTag().
    55  // Paths are strings using dot-notation.
    56  func BytePathForTagShortest(doc []byte, key string) (string, error) {
    57  	m, err := ByteDocToMap(doc)
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	s := PathForKeyShortest(m, key)
    63  	return s, nil
    64  }
    65  
    66  // Get all paths through the map (in dot-notation) that terminate with the specified key.
    67  // Results can be used with ValuesAtKeyPath() and ValuesFromKeyPath().
    68  func PathsForKey(m map[string]interface{}, key string) []string {
    69  	breadbasket := make(map[string]bool,0)
    70  	breadcrumb := ""
    71  
    72  	hasKeyPath(breadcrumb, m, key, &breadbasket)
    73  	if len(breadbasket) == 0 {
    74  		return nil
    75  	}
    76  
    77  	// unpack map keys to return
    78  	res := make([]string,len(breadbasket))
    79  	var i int
    80  	for k,_ := range breadbasket {
    81  		res[i] = k
    82  		i++
    83  	}
    84  
    85  	return res
    86  }
    87  
    88  // Extract the shortest path from all possible paths - from PathsForKey().
    89  // Paths are strings using dot-notation.
    90  func PathForKeyShortest(m map[string]interface{}, key string) string {
    91  	paths := PathsForKey(m,key)
    92  
    93  	lp := len(paths)
    94  	if lp == 0 {
    95  		return ""
    96  	}
    97  	if lp == 1 {
    98  		return paths[0]
    99  	}
   100  
   101  	shortest := paths[0]
   102  	shortestLen := len(strings.Split(shortest,"."))
   103  
   104  	for i := 1 ; i < len(paths) ; i++ {
   105  		vlen := len(strings.Split(paths[i],"."))
   106  		if vlen < shortestLen {
   107  			shortest = paths[i]
   108  			shortestLen = vlen
   109  		}
   110  	}
   111  
   112  	return shortest
   113  }
   114  
   115  // hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth
   116  // This is really just a breadcrumber that saves all trails that hit the prescribed 'key'.
   117  func hasKeyPath(crumb string, iv interface{}, key string, basket *map[string]bool) {
   118  	switch iv.(type) {
   119  	case map[string]interface{}:
   120  		vv := iv.(map[string]interface{})
   121  		if _, ok := vv[key]; ok {
   122  			if crumb == "" {
   123  				crumb = key
   124  			} else {
   125  				crumb += "." + key
   126  			}
   127  			// *basket = append(*basket, crumb)
   128  			(*basket)[crumb] = true
   129  		}
   130  		// walk on down the path, key could occur again at deeper node
   131  		for k, v := range vv {
   132  			// create a new breadcrumb, add the one we're at to the crumb-trail
   133  			var nbc string
   134  			if crumb == "" {
   135  				nbc = k
   136  			} else {
   137  				nbc = crumb + "." + k
   138  			}
   139  			hasKeyPath(nbc, v, key, basket)
   140  		}
   141  	case []interface{}:
   142  		// crumb-trail doesn't change, pass it on
   143  		for _, v := range iv.([]interface{}) {
   144  			hasKeyPath(crumb, v, key, basket)
   145  		}
   146  	}
   147  }
   148  
   149  

View as plain text