keyvalues.go raw

   1  // Copyright 2012-2014 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  //	keyvalues.go: Extract values from an arbitrary XML doc. Tag path can include wildcard characters.
   6  
   7  package mxj
   8  
   9  import (
  10  	"errors"
  11  	"fmt"
  12  	"strconv"
  13  	"strings"
  14  )
  15  
  16  // ----------------------------- get everything FOR a single key -------------------------
  17  
  18  const (
  19  	minArraySize = 32
  20  )
  21  
  22  var defaultArraySize int = minArraySize
  23  
  24  // SetArraySize adjust the buffers for expected number of values to return from ValuesForKey() and ValuesForPath().
  25  // This can have the effect of significantly reducing memory allocation-copy functions for large data sets.
  26  // Returns the initial buffer size.
  27  func SetArraySize(size int) int {
  28  	if size > minArraySize {
  29  		defaultArraySize = size
  30  	} else {
  31  		defaultArraySize = minArraySize
  32  	}
  33  	return defaultArraySize
  34  }
  35  
  36  // ValuesForKey return all values in Map, 'mv', associated with a 'key'. If len(returned_values) == 0, then no match.
  37  // On error, the returned slice is 'nil'. NOTE: 'key' can be wildcard, "*".
  38  //   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
  39  //             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
  40  //             - For attributes prefix the label with the attribute prefix character, by default a 
  41  //               hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
  42  //             - If the 'key' refers to a list, then "key:value" could select a list member of the list.
  43  //             - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
  44  //             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
  45  //               exclusion critera - e.g., "!author:William T. Gaddis".
  46  //             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
  47  func (mv Map) ValuesForKey(key string, subkeys ...string) ([]interface{}, error) {
  48  	m := map[string]interface{}(mv)
  49  	var subKeyMap map[string]interface{}
  50  	if len(subkeys) > 0 {
  51  		var err error
  52  		subKeyMap, err = getSubKeyMap(subkeys...)
  53  		if err != nil {
  54  			return nil, err
  55  		}
  56  	}
  57  
  58  	ret := make([]interface{}, 0, defaultArraySize)
  59  	var cnt int
  60  	hasKey(m, key, &ret, &cnt, subKeyMap)
  61  	return ret[:cnt], nil
  62  }
  63  
  64  var KeyNotExistError = errors.New("Key does not exist")
  65  
  66  // ValueForKey is a wrapper on ValuesForKey.  It returns the first member of []interface{}, if any.
  67  // If there is no value, "nil, nil" is returned.
  68  func (mv Map) ValueForKey(key string, subkeys ...string) (interface{}, error) {
  69  	vals, err := mv.ValuesForKey(key, subkeys...)
  70  	if err != nil {
  71  		return nil, err
  72  	}
  73  	if len(vals) == 0 {
  74  		return nil, KeyNotExistError
  75  	}
  76  	return vals[0], nil
  77  }
  78  
  79  // hasKey - if the map 'key' exists append it to array
  80  //          if it doesn't do nothing except scan array and map values
  81  func hasKey(iv interface{}, key string, ret *[]interface{}, cnt *int, subkeys map[string]interface{}) {
  82  	// func hasKey(iv interface{}, key string, ret *[]interface{}, subkeys map[string]interface{}) {
  83  	switch iv.(type) {
  84  	case map[string]interface{}:
  85  		vv := iv.(map[string]interface{})
  86  		// see if the current value is of interest
  87  		if v, ok := vv[key]; ok {
  88  			switch v.(type) {
  89  			case map[string]interface{}:
  90  				if hasSubKeys(v, subkeys) {
  91  					*ret = append(*ret, v)
  92  					*cnt++
  93  				}
  94  			case []interface{}:
  95  				for _, av := range v.([]interface{}) {
  96  					if hasSubKeys(av, subkeys) {
  97  						*ret = append(*ret, av)
  98  						*cnt++
  99  					}
 100  				}
 101  			default:
 102  				if len(subkeys) == 0 {
 103  					*ret = append(*ret, v)
 104  					*cnt++
 105  				}
 106  			}
 107  		}
 108  
 109  		// wildcard case
 110  		if key == "*" {
 111  			for _, v := range vv {
 112  				switch v.(type) {
 113  				case map[string]interface{}:
 114  					if hasSubKeys(v, subkeys) {
 115  						*ret = append(*ret, v)
 116  						*cnt++
 117  					}
 118  				case []interface{}:
 119  					for _, av := range v.([]interface{}) {
 120  						if hasSubKeys(av, subkeys) {
 121  							*ret = append(*ret, av)
 122  							*cnt++
 123  						}
 124  					}
 125  				default:
 126  					if len(subkeys) == 0 {
 127  						*ret = append(*ret, v)
 128  						*cnt++
 129  					}
 130  				}
 131  			}
 132  		}
 133  
 134  		// scan the rest
 135  		for _, v := range vv {
 136  			hasKey(v, key, ret, cnt, subkeys)
 137  		}
 138  	case []interface{}:
 139  		for _, v := range iv.([]interface{}) {
 140  			hasKey(v, key, ret, cnt, subkeys)
 141  		}
 142  	}
 143  }
 144  
 145  // -----------------------  get everything for a node in the Map ---------------------------
 146  
 147  // Allow indexed arrays in "path" specification. (Request from Abhijit Kadam - abhijitk100@gmail.com.)
 148  // 2014.04.28 - implementation note.
 149  // Implemented as a wrapper of (old)ValuesForPath() because we need look-ahead logic to handle expansion
 150  // of wildcards and unindexed arrays.  Embedding such logic into valuesForKeyPath() would have made the
 151  // code much more complicated; this wrapper is straightforward, easy to debug, and doesn't add significant overhead.
 152  
 153  // ValuesForPatb retrieves all values for a path from the Map.  If len(returned_values) == 0, then no match.
 154  // On error, the returned array is 'nil'.
 155  //   'path' is a dot-separated path of key values.
 156  //          - If a node in the path is '*', then everything beyond is walked.
 157  //          - 'path' can contain indexed array references, such as, "*.data[1]" and "msgs[2].data[0].field" -
 158  //            even "*[2].*[0].field".
 159  //   'subkeys' (optional) are "key:val[:type]" strings representing attributes or elements in a list.
 160  //             - By default 'val' is of type string. "key:val:bool" and "key:val:float" to coerce them.
 161  //             - For attributes prefix the label with the attribute prefix character, by default a 
 162  //               hyphen, '-', e.g., "-seq:3". (See SetAttrPrefix function.)
 163  //             - If the 'path' refers to a list, then "tag:value" would return member of the list.
 164  //             - The subkey can be wildcarded - "key:*" - to require that it's there with some value.
 165  //             - If a subkey is preceeded with the '!' character, the key:value[:type] entry is treated as an
 166  //               exclusion critera - e.g., "!author:William T. Gaddis".
 167  //             - If val contains ":" symbol, use SetFieldSeparator to a unused symbol, perhaps "|".
 168  func (mv Map) ValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
 169  	// If there are no array indexes in path, use legacy ValuesForPath() logic.
 170  	if strings.Index(path, "[") < 0 {
 171  		return mv.oldValuesForPath(path, subkeys...)
 172  	}
 173  
 174  	var subKeyMap map[string]interface{}
 175  	if len(subkeys) > 0 {
 176  		var err error
 177  		subKeyMap, err = getSubKeyMap(subkeys...)
 178  		if err != nil {
 179  			return nil, err
 180  		}
 181  	}
 182  
 183  	keys, kerr := parsePath(path)
 184  	if kerr != nil {
 185  		return nil, kerr
 186  	}
 187  
 188  	vals, verr := valuesForArray(keys, mv)
 189  	if verr != nil {
 190  		return nil, verr // Vals may be nil, but return empty array.
 191  	}
 192  
 193  	// Need to handle subkeys ... only return members of vals that satisfy conditions.
 194  	retvals := make([]interface{}, 0)
 195  	for _, v := range vals {
 196  		if hasSubKeys(v, subKeyMap) {
 197  			retvals = append(retvals, v)
 198  		}
 199  	}
 200  	return retvals, nil
 201  }
 202  
 203  func valuesForArray(keys []*key, m Map) ([]interface{}, error) {
 204  	var tmppath string
 205  	var haveFirst bool
 206  	var vals []interface{}
 207  	var verr error
 208  
 209  	lastkey := len(keys) - 1
 210  	for i := 0; i <= lastkey; i++ {
 211  		if !haveFirst {
 212  			tmppath = keys[i].name
 213  			haveFirst = true
 214  		} else {
 215  			tmppath += "." + keys[i].name
 216  		}
 217  
 218  		// Look-ahead: explode wildcards and unindexed arrays.
 219  		// Need to handle un-indexed list recursively:
 220  		// e.g., path is "stuff.data[0]" rather than "stuff[0].data[0]".
 221  		// Need to treat it as "stuff[0].data[0]", "stuff[1].data[0]", ...
 222  		if !keys[i].isArray && i < lastkey && keys[i+1].isArray {
 223  			// Can't pass subkeys because we may not be at literal end of path.
 224  			vv, vverr := m.oldValuesForPath(tmppath)
 225  			if vverr != nil {
 226  				return nil, vverr
 227  			}
 228  			for _, v := range vv {
 229  				// See if we can walk the value.
 230  				am, ok := v.(map[string]interface{})
 231  				if !ok {
 232  					continue
 233  				}
 234  				// Work the backend.
 235  				nvals, nvalserr := valuesForArray(keys[i+1:], Map(am))
 236  				if nvalserr != nil {
 237  					return nil, nvalserr
 238  				}
 239  				vals = append(vals, nvals...)
 240  			}
 241  			break // have recursed the whole path - return
 242  		}
 243  
 244  		if keys[i].isArray || i == lastkey {
 245  			// Don't pass subkeys because may not be at literal end of path.
 246  			vals, verr = m.oldValuesForPath(tmppath)
 247  		} else {
 248  			continue
 249  		}
 250  		if verr != nil {
 251  			return nil, verr
 252  		}
 253  
 254  		if i == lastkey && !keys[i].isArray {
 255  			break
 256  		}
 257  
 258  		// Now we're looking at an array - supposedly.
 259  		// Is index in range of vals?
 260  		if len(vals) <= keys[i].position {
 261  			vals = nil
 262  			break
 263  		}
 264  
 265  		// Return the array member of interest, if at end of path.
 266  		if i == lastkey {
 267  			vals = vals[keys[i].position:(keys[i].position + 1)]
 268  			break
 269  		}
 270  
 271  		// Extract the array member of interest.
 272  		am := vals[keys[i].position:(keys[i].position + 1)]
 273  
 274  		// must be a map[string]interface{} value so we can keep walking the path
 275  		amm, ok := am[0].(map[string]interface{})
 276  		if !ok {
 277  			vals = nil
 278  			break
 279  		}
 280  
 281  		m = Map(amm)
 282  		haveFirst = false
 283  	}
 284  
 285  	return vals, nil
 286  }
 287  
 288  type key struct {
 289  	name     string
 290  	isArray  bool
 291  	position int
 292  }
 293  
 294  func parsePath(s string) ([]*key, error) {
 295  	keys := strings.Split(s, ".")
 296  
 297  	ret := make([]*key, 0)
 298  
 299  	for i := 0; i < len(keys); i++ {
 300  		if keys[i] == "" {
 301  			continue
 302  		}
 303  
 304  		newkey := new(key)
 305  		if strings.Index(keys[i], "[") < 0 {
 306  			newkey.name = keys[i]
 307  			ret = append(ret, newkey)
 308  			continue
 309  		}
 310  
 311  		p := strings.Split(keys[i], "[")
 312  		newkey.name = p[0]
 313  		p = strings.Split(p[1], "]")
 314  		if p[0] == "" { // no right bracket
 315  			return nil, fmt.Errorf("no right bracket on key index: %s", keys[i])
 316  		}
 317  		// convert p[0] to a int value
 318  		pos, nerr := strconv.ParseInt(p[0], 10, 32)
 319  		if nerr != nil {
 320  			return nil, fmt.Errorf("cannot convert index to int value: %s", p[0])
 321  		}
 322  		newkey.position = int(pos)
 323  		newkey.isArray = true
 324  		ret = append(ret, newkey)
 325  	}
 326  
 327  	return ret, nil
 328  }
 329  
 330  // legacy ValuesForPath() - now wrapped to handle special case of indexed arrays in 'path'.
 331  func (mv Map) oldValuesForPath(path string, subkeys ...string) ([]interface{}, error) {
 332  	m := map[string]interface{}(mv)
 333  	var subKeyMap map[string]interface{}
 334  	if len(subkeys) > 0 {
 335  		var err error
 336  		subKeyMap, err = getSubKeyMap(subkeys...)
 337  		if err != nil {
 338  			return nil, err
 339  		}
 340  	}
 341  
 342  	keys := strings.Split(path, ".")
 343  	if keys[len(keys)-1] == "" {
 344  		keys = keys[:len(keys)-1]
 345  	}
 346  	ivals := make([]interface{}, 0, defaultArraySize)
 347  	var cnt int
 348  	valuesForKeyPath(&ivals, &cnt, m, keys, subKeyMap)
 349  	return ivals[:cnt], nil
 350  }
 351  
 352  func valuesForKeyPath(ret *[]interface{}, cnt *int, m interface{}, keys []string, subkeys map[string]interface{}) {
 353  	lenKeys := len(keys)
 354  
 355  	// load 'm' values into 'ret'
 356  	// expand any lists
 357  	if lenKeys == 0 {
 358  		switch m.(type) {
 359  		case map[string]interface{}:
 360  			if subkeys != nil {
 361  				if ok := hasSubKeys(m, subkeys); !ok {
 362  					return
 363  				}
 364  			}
 365  			*ret = append(*ret, m)
 366  			*cnt++
 367  		case []interface{}:
 368  			for i, v := range m.([]interface{}) {
 369  				if subkeys != nil {
 370  					if ok := hasSubKeys(v, subkeys); !ok {
 371  						continue // only load list members with subkeys
 372  					}
 373  				}
 374  				*ret = append(*ret, (m.([]interface{}))[i])
 375  				*cnt++
 376  			}
 377  		default:
 378  			if subkeys != nil {
 379  				return // must be map[string]interface{} if there are subkeys
 380  			}
 381  			*ret = append(*ret, m)
 382  			*cnt++
 383  		}
 384  		return
 385  	}
 386  
 387  	// key of interest
 388  	key := keys[0]
 389  	switch key {
 390  	case "*": // wildcard - scan all values
 391  		switch m.(type) {
 392  		case map[string]interface{}:
 393  			for _, v := range m.(map[string]interface{}) {
 394  				// valuesForKeyPath(ret, v, keys[1:], subkeys)
 395  				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
 396  			}
 397  		case []interface{}:
 398  			for _, v := range m.([]interface{}) {
 399  				switch v.(type) {
 400  				// flatten out a list of maps - keys are processed
 401  				case map[string]interface{}:
 402  					for _, vv := range v.(map[string]interface{}) {
 403  						// valuesForKeyPath(ret, vv, keys[1:], subkeys)
 404  						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
 405  					}
 406  				default:
 407  					// valuesForKeyPath(ret, v, keys[1:], subkeys)
 408  					valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
 409  				}
 410  			}
 411  		}
 412  	default: // key - must be map[string]interface{}
 413  		switch m.(type) {
 414  		case map[string]interface{}:
 415  			if v, ok := m.(map[string]interface{})[key]; ok {
 416  				// valuesForKeyPath(ret, v, keys[1:], subkeys)
 417  				valuesForKeyPath(ret, cnt, v, keys[1:], subkeys)
 418  			}
 419  		case []interface{}: // may be buried in list
 420  			for _, v := range m.([]interface{}) {
 421  				switch v.(type) {
 422  				case map[string]interface{}:
 423  					if vv, ok := v.(map[string]interface{})[key]; ok {
 424  						// valuesForKeyPath(ret, vv, keys[1:], subkeys)
 425  						valuesForKeyPath(ret, cnt, vv, keys[1:], subkeys)
 426  					}
 427  				}
 428  			}
 429  		}
 430  	}
 431  }
 432  
 433  // hasSubKeys() - interface{} equality works for string, float64, bool
 434  // 'v' must be a map[string]interface{} value to have subkeys
 435  // 'a' can have k:v pairs with v.(string) == "*", which is treated like a wildcard.
 436  func hasSubKeys(v interface{}, subkeys map[string]interface{}) bool {
 437  	if len(subkeys) == 0 {
 438  		return true
 439  	}
 440  
 441  	switch v.(type) {
 442  	case map[string]interface{}:
 443  		// do all subKey name:value pairs match?
 444  		mv := v.(map[string]interface{})
 445  		for skey, sval := range subkeys {
 446  			isNotKey := false
 447  			if skey[:1] == "!" { // a NOT-key
 448  				skey = skey[1:]
 449  				isNotKey = true
 450  			}
 451  			vv, ok := mv[skey]
 452  			if !ok { // key doesn't exist
 453  				if isNotKey { // key not there, but that's what we want
 454  					if kv, ok := sval.(string); ok && kv == "*" {
 455  						continue
 456  					}
 457  				}
 458  				return false
 459  			}
 460  			// wildcard check
 461  			if kv, ok := sval.(string); ok && kv == "*" {
 462  				if isNotKey { // key is there, and we don't want it
 463  					return false
 464  				}
 465  				continue
 466  			}
 467  			switch sval.(type) {
 468  			case string:
 469  				if s, ok := vv.(string); ok && s == sval.(string) {
 470  					if isNotKey {
 471  						return false
 472  					}
 473  					continue
 474  				}
 475  			case bool:
 476  				if b, ok := vv.(bool); ok && b == sval.(bool) {
 477  					if isNotKey {
 478  						return false
 479  					}
 480  					continue
 481  				}
 482  			case float64:
 483  				if f, ok := vv.(float64); ok && f == sval.(float64) {
 484  					if isNotKey {
 485  						return false
 486  					}
 487  					continue
 488  				}
 489  			}
 490  			// key there but didn't match subkey value
 491  			if isNotKey { // that's what we want
 492  				continue
 493  			}
 494  			return false
 495  		}
 496  		// all subkeys matched
 497  		return true
 498  	}
 499  
 500  	// not a map[string]interface{} value, can't have subkeys
 501  	return false
 502  }
 503  
 504  // Generate map of key:value entries as map[string]string.
 505  //	'kv' arguments are "name:value" pairs: attribute keys are designated with prepended hyphen, '-'.
 506  //	If len(kv) == 0, the return is (nil, nil).
 507  func getSubKeyMap(kv ...string) (map[string]interface{}, error) {
 508  	if len(kv) == 0 {
 509  		return nil, nil
 510  	}
 511  	m := make(map[string]interface{}, 0)
 512  	for _, v := range kv {
 513  		vv := strings.Split(v, fieldSep)
 514  		switch len(vv) {
 515  		case 2:
 516  			m[vv[0]] = interface{}(vv[1])
 517  		case 3:
 518  			switch vv[2] {
 519  			case "string", "char", "text":
 520  				m[vv[0]] = interface{}(vv[1])
 521  			case "bool", "boolean":
 522  				// ParseBool treats "1"==true & "0"==false
 523  				b, err := strconv.ParseBool(vv[1])
 524  				if err != nil {
 525  					return nil, fmt.Errorf("can't convert subkey value to bool: %s", vv[1])
 526  				}
 527  				m[vv[0]] = interface{}(b)
 528  			case "float", "float64", "num", "number", "numeric":
 529  				f, err := strconv.ParseFloat(vv[1], 64)
 530  				if err != nil {
 531  					return nil, fmt.Errorf("can't convert subkey value to float: %s", vv[1])
 532  				}
 533  				m[vv[0]] = interface{}(f)
 534  			default:
 535  				return nil, fmt.Errorf("unknown subkey conversion spec: %s", v)
 536  			}
 537  		default:
 538  			return nil, fmt.Errorf("unknown subkey spec: %s", v)
 539  		}
 540  	}
 541  	return m, nil
 542  }
 543  
 544  // -------------------------------  END of valuesFor ... ----------------------------
 545  
 546  // ----------------------- locate where a key value is in the tree -------------------
 547  
 548  //----------------------------- find all paths to a key --------------------------------
 549  
 550  // PathsForKey returns all paths through Map, 'mv', (in dot-notation) that terminate with the specified key.
 551  // Results can be used with ValuesForPath.
 552  func (mv Map) PathsForKey(key string) []string {
 553  	m := map[string]interface{}(mv)
 554  	breadbasket := make(map[string]bool, 0)
 555  	breadcrumbs := ""
 556  
 557  	hasKeyPath(breadcrumbs, m, key, breadbasket)
 558  	if len(breadbasket) == 0 {
 559  		return nil
 560  	}
 561  
 562  	// unpack map keys to return
 563  	res := make([]string, len(breadbasket))
 564  	var i int
 565  	for k := range breadbasket {
 566  		res[i] = k
 567  		i++
 568  	}
 569  
 570  	return res
 571  }
 572  
 573  // PathForKeyShortest extracts the shortest path from all possible paths - from PathsForKey() - in Map, 'mv'..
 574  // Paths are strings using dot-notation.
 575  func (mv Map) PathForKeyShortest(key string) string {
 576  	paths := mv.PathsForKey(key)
 577  
 578  	lp := len(paths)
 579  	if lp == 0 {
 580  		return ""
 581  	}
 582  	if lp == 1 {
 583  		return paths[0]
 584  	}
 585  
 586  	shortest := paths[0]
 587  	shortestLen := len(strings.Split(shortest, "."))
 588  
 589  	for i := 1; i < len(paths); i++ {
 590  		vlen := len(strings.Split(paths[i], "."))
 591  		if vlen < shortestLen {
 592  			shortest = paths[i]
 593  			shortestLen = vlen
 594  		}
 595  	}
 596  
 597  	return shortest
 598  }
 599  
 600  // hasKeyPath - if the map 'key' exists append it to KeyPath.path and increment KeyPath.depth
 601  // This is really just a breadcrumber that saves all trails that hit the prescribed 'key'.
 602  func hasKeyPath(crumbs string, iv interface{}, key string, basket map[string]bool) {
 603  	switch iv.(type) {
 604  	case map[string]interface{}:
 605  		vv := iv.(map[string]interface{})
 606  		if _, ok := vv[key]; ok {
 607  			// create a new breadcrumb, intialized with the one we have
 608  			var nbc string
 609  			if crumbs == "" {
 610  				nbc = key
 611  			} else {
 612  				nbc = crumbs + "." + key
 613  			}
 614  			basket[nbc] = true
 615  		}
 616  		// walk on down the path, key could occur again at deeper node
 617  		for k, v := range vv {
 618  			// create a new breadcrumb, intialized with the one we have
 619  			var nbc string
 620  			if crumbs == "" {
 621  				nbc = k
 622  			} else {
 623  				nbc = crumbs + "." + k
 624  			}
 625  			hasKeyPath(nbc, v, key, basket)
 626  		}
 627  	case []interface{}:
 628  		// crumb-trail doesn't change, pass it on
 629  		for _, v := range iv.([]interface{}) {
 630  			hasKeyPath(crumbs, v, key, basket)
 631  		}
 632  	}
 633  }
 634  
 635  var PathNotExistError = errors.New("Path does not exist")
 636  
 637  // ValueForPath wraps ValuesFor Path and returns the first value returned.
 638  // If no value is found it returns 'nil' and PathNotExistError.
 639  func (mv Map) ValueForPath(path string) (interface{}, error) {
 640  	vals, err := mv.ValuesForPath(path)
 641  	if err != nil {
 642  		return nil, err
 643  	}
 644  	if len(vals) == 0 {
 645  		return nil, PathNotExistError
 646  	}
 647  	return vals[0], nil
 648  }
 649  
 650  // ValuesForPathString returns the first found value for the path as a string.
 651  func (mv Map) ValueForPathString(path string) (string, error) {
 652  	vals, err := mv.ValuesForPath(path)
 653  	if err != nil {
 654  		return "", err
 655  	}
 656  	if len(vals) == 0 {
 657  		return "", errors.New("ValueForPath: path not found")
 658  	}
 659  	val := vals[0]
 660  	return fmt.Sprintf("%v", val), nil
 661  }
 662  
 663  // ValueOrEmptyForPathString returns the first found value for the path as a string.
 664  // If the path is not found then it returns an empty string.
 665  func (mv Map) ValueOrEmptyForPathString(path string) string {
 666  	str, _ := mv.ValueForPathString(path)
 667  	return str
 668  }
 669