【发布时间】:2011-08-31 08:30:04
【问题描述】:
我有一个类似这样的字符串散列:
Map map = ['a.b.c': 'Hi']
...我需要在 gradle 中使用来扩展这样的表达式:
This is a greeting: ${a.b.c}
如果我将 gradle 复制任务与 expand 一起使用,我将收到一条错误消息“没有这样的属性:a”。
有没有办法让 gradle/groovy 将该映射转换为我需要解析的属性?
【问题讨论】:
我有一个类似这样的字符串散列:
Map map = ['a.b.c': 'Hi']
...我需要在 gradle 中使用来扩展这样的表达式:
This is a greeting: ${a.b.c}
如果我将 gradle 复制任务与 expand 一起使用,我将收到一条错误消息“没有这样的属性:a”。
有没有办法让 gradle/groovy 将该映射转换为我需要解析的属性?
【问题讨论】:
我找不到内置的答案,但这是一个完整的、自包含的方法,可以用作 Map 上的元方法来做你想做的事:
Map map = ['a.b.c': 'Hi', 'a.b.d': 'Yo', 'f.g.h': 'Howdy']
Map.metaClass.expandKeys = { separator = '.' ->
def mergeMaps = { a, b ->
b.each{ k, v ->
if(a[k] && (v instanceof Map)) {
mergeMaps(a[k], v)
} else {
a[k] = v
}
}
a
}
delegate.inject([:]){ result, k, v ->
mergeMaps(result, k.tokenize(separator).reverse().inject(v){last, subkey -> [(subkey):last] })
}
}
assert map.expandKeys() == [a:[b:[c:"Hi", d:"Yo"]], f:[g:[h:"Howdy"]]]
它还允许使用不同于. 的分隔符,只需将分隔符传递给expandKeys 方法
如果你想像普通函数一样使用它,那么你可以这样做:
Map map = ['a.b.c': 'Hi', 'a.b.d': 'Yo', 'f.g.h': 'Howdy']
def expandKeys = { Map input, separator = '.' ->
def mergeMaps = { a, b ->
b.each{ k, v ->
if(a[k] && (v instanceof Map)) {
mergeMaps(a[k], v)
} else {
a[k] = v
}
}
a
}
input.inject([:]){ result, k, v ->
mergeMaps(result, k.tokenize(separator).reverse().inject(v){last, subkey -> [(subkey):last] })
}
}
assert expandKeys(map) == [a:[b:[c:"Hi", d:"Yo"]], f:[g:[h:"Howdy"]]]
除了合并地图之外,主要技巧是拆分然后反转每个键。然后可以向后建立最终的层次结构。另外,可能有更好的方法来处理合并,因为我不喜欢挂在最后的a。
【讨论】:
我对 Gradle 一无所知,但也许这会有所帮助....
如果你有地图
Map map = ['a.b.c': 'Hi']
那么您无法使用检索值“Hi”
map.a.b.c
相反,您必须使用:
map.'a.b.c'
或
map['a.b.c']
【讨论】:
我不完全确定您的需求是什么,但如果您只需要替换类似属性的标记并且不需要 Groovy 模板的全部功能,则可以将 filter() 方法与 Ant 的 ReplaceTokens 类结合使用比expand() 更安全(更快)。请参阅 Gradle 用户指南中的 Filtering files。
【讨论】: