groovy嵌套地图更新值,而不检查值的类型

问题描述

我有两张地图,

def configmap = [ "env" :["required": ["team" : "k","test": "sample"],"optional" : ["anotherkey" : ""]]]
def valuesReplacementMap=[ "env.required.team":"s","env.required.test":"new","env.optional.anotherkey":"newvalue" ]

valuesReplacementMap具有指向configmap中的键的路径以及需要更新的新值。

我需要将configmap中的值更新为valuesReplacementMap中给出的值

这是我的代码,可以正常工作


def m = [ "env" :["required": ["team" : "k","env.optional.anotherkey":"newvalue" ]

def copyofOrigMap
valuesReplacementMap.each{ key,value->
    copyofOrigMap = m
    key.tokenize(".").inject{ attr,val ->
        if(copyofOrigMap[attr][val] instanceof java.lang.String ){
            copyofOrigMap[attr][val] = value
        }else 
            copyofOrigMap = copyofOrigMap[attr]
        val
    }
}

我的问题是

  1. 有一种避免检查instanceof java.lang.String方法,我正在这样做以查看是否达到了叶节点/密钥
  2. 有没有更简单的方法来实现全部功能

我经历了一些较早发布的问题,但是没有找到任何优化的方法

  1. Updating value in a map while iterating the map groovy

  2. Groovy - Change value of string in nested map

  3. Updating groovy object fields from a map

解决方法

解决此问题的一种方法是Eval.me。以下代码:

def m = [ "env" :["required": ["team" : "k","test": "sample"],"optional" : ["anotherkey" : ""]]]
def valuesReplacementMap=[ "env.required.team":"s","env.required.test":"new","env.optional.anotherkey":"newvalue" ]

valuesReplacementMap.each { k,v -> 
  Eval.me('m',m,"m.${k} = '${v}'")
}

m.each { k,v -> 
  println "$k -> $v"
}

执行时结果为:

─➤ groovy solution.groovy                                          
env -> [required:[team:s,test:new],optional:[anotherkey:newvalue]]
─➤ 

(在Groovy版本上:3.0.5 JVM:11.0.8供应商:Amazon.com Inc.操作系统:Linux)

其中'm'方法的第一个参数Eval.me告诉eval我们想要在字符串表达式中绑定到名称m的值,第二个参数是值本身。第三个参数是要求值的表达式。

可能有比eval更优雅的方法,但这很简洁,可以为您提供所需的结果。

还应注意,这仅适用于字符串值,并且从安全角度考虑,如果任何键或值来自用户输入,则使用Eval可能很危险。

修改可能会带来更好的安全隐患

您也可以执行以下操作:

def m = [ "env" :["required": ["team" : "k","env.optional.anotherkey":"newvalue" ]


valuesReplacementMap.each { k,v -> 
  def tokens = k.tokenize('.')
  def last = tokens.init().inject(m) { a,t -> a[t] }
  last[tokens.last()] = v
}

m.each { k,v -> 
  println "$k -> $v"
}

产生的输出与上面的Eval.me示例相同。