【问题标题】:Dotted strings to properties属性的虚线字符串
【发布时间】: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 将该映射转换为我需要解析的属性?

【问题讨论】:

    标签: groovy gradle


    【解决方案1】:

    我找不到内置的答案,但这是一个完整的、自包含的方法,可以用作 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 或 groovysh 中运行它时遇到了一些麻烦。我收到错误“groovy.lang.MissingPropertyException”。由于复杂性(即你不能同时拥有 a 和 a.b),我决定根本不这样做。
    【解决方案2】:

    我对 Gradle 一无所知,但也许这会有所帮助....

    如果你有地图

    Map map = ['a.b.c': 'Hi']
    

    那么您无法使用检索值“Hi”

    map.a.b.c
    

    相反,您必须使用:

    map.'a.b.c'
    

    map['a.b.c']
    

    【讨论】:

    • 是的;那是我原来的问题。但是,我认为这一定是一个普遍的问题,有一个解决方案;可能是建造者的形式。
    【解决方案3】:

    我不完全确定您的需求是什么,但如果您只需要替换类似属性的标记并且不需要 Groovy 模板的全部功能,则可以将 filter() 方法与 Ant 的 ReplaceTokens 类结合使用比expand() 更安全(更快)。请参阅 Gradle 用户指南中的 Filtering files

    【讨论】:

    • 过滤器不接受多字符标记。但是,最终使扩展工作的麻烦超过了更改所有令牌的麻烦。
    猜你喜欢
    • 1970-01-01
    • 2017-07-16
    • 2017-07-13
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多