rename.go 1.5 KB

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