rename.go raw

   1  package mxj
   2  
   3  import (
   4  	"errors"
   5  	"strings"
   6  )
   7  
   8  // RenameKey renames a key in a Map.
   9  // It works only for nested maps. 
  10  // It doesn't work for cases when the key is in a list.
  11  func (mv Map) RenameKey(path string, newName string) error {
  12  	var v bool
  13  	var err error
  14  	if v, err = mv.Exists(path); err == nil && !v {
  15  		return errors.New("RenameKey: path not found: " + path)
  16  	} else if err != nil {
  17  		return err
  18  	}
  19  	if v, err = mv.Exists(parentPath(path) + "." + newName); err == nil && v {
  20  		return errors.New("RenameKey: key already exists: " + newName)
  21  	} else if err != nil {
  22  		return err
  23  	}
  24  
  25  	m := map[string]interface{}(mv)
  26  	return renameKey(m, path, newName)
  27  }
  28  
  29  func renameKey(m interface{}, path string, newName string) error {
  30  	val, err := prevValueByPath(m, path)
  31  	if err != nil {
  32  		return err
  33  	}
  34  
  35  	oldName := lastKey(path)
  36  	val[newName] = val[oldName]
  37  	delete(val, oldName)
  38  
  39  	return nil
  40  }
  41  
  42  // returns a value which contains a last key in the path
  43  // For example: prevValueByPath("a.b.c", {a{b{c: 3}}}) returns {c: 3}
  44  func prevValueByPath(m interface{}, path string) (map[string]interface{}, error) {
  45  	keys := strings.Split(path, ".")
  46  
  47  	switch mValue := m.(type) {
  48  	case map[string]interface{}:
  49  		for key, value := range mValue {
  50  			if key == keys[0] {
  51  				if len(keys) == 1 {
  52  					return mValue, nil
  53  				} else {
  54  					// keep looking for the full path to the key
  55  					return prevValueByPath(value, strings.Join(keys[1:], "."))
  56  				}
  57  			}
  58  		}
  59  	}
  60  	return nil, errors.New("prevValueByPath: didn't find path – " + path)
  61  }
  62