【问题标题】:Groovy: isn't there a stringToMap out of the box?Groovy:没有开箱即用的 stringToMap 吗?
【发布时间】:2011-01-13 19:58:18
【问题描述】:

作为一个从 groovy 开始的 tcl 开发人员,我对 groovy 中的列表和映射支持有点惊讶。也许我在这里遗漏了一些东西。

我习惯于在 tcl 中即时在字符串、列表和数组/映射之间进行转换。在 tcl 中,类似

"['a':2,'b':4]".each {key, value -> println key + " " + value}

有可能,就像在 groovy 中一样,每个命令都会遍历字符串的每个字符。

这将是一个很大的问题,因为我可以轻松地使用 split 或 tokenize 命令之类的东西,但是因为序列化的列表或映射不仅仅是“a:2,b:4”,它有点难解析。

似乎 griffon 开发人员使用 stringToMap 库 (http://code.google.com/p/stringtomap/),但该示例也无法处理序列化地图。

所以我现在的问题是:在 groovy 中解析地图或列表的最佳方法是什么?

干杯, 拉尔夫

PS:这是一个很时髦的问题,但我已经用 grails 标记了它,因为我需要 grails 的这个功能,我想通过 URL 传递地图

更新:这对我来说仍然是一个悬而未决的问题......所以这里有一些更新给那些有同样问题的人:

  • 当您将 Map 转换为 String 时,.toString() 将导致在所有情况下都无法转换回 Map,但 .inspect() 将为您提供一个 String 可以评估回一张地图!
  • 在 Grails 中,有一个 .encodeAsJSON()JSON.parse(String) - 两者都很好用,但我还没有检查解析器将如何处理 JSON 函数(可能存在安全问题)

【问题讨论】:

  • 如果您使用 grails 并想要一张地图,我会考虑发布一条 JSON 消息。可能更容易在客户端生成,并且 grails 中内置了一些东西来评估 JSON。
  • 谢谢。 json 确实可以是一个很好的选择!
  • 在 Grails 中通过 URL 传递映射听起来像是 URL 映射的工作。查看 grails 用户指南的嵌入式变量部分(第 6.4.2 节)。你可以定义一个自定义的 URL 结构来传递你想要的任何地图,即myapp.com/controller/action/key1/value1/key2/value2 它对于多维地图或巨大的数据结构来说效果不佳,但我认为这些不应该通过网址。
  • 好主意 - 当你有小地图时。就我而言,我想通过 URL 提交要在图表上绘制的数据。类似于谷歌图表 API 的东西。所以我的地图可能会很大...

标签: string grails groovy map params


【解决方案1】:

希望对您有所帮助:

    foo= "['a':2,'b':4]"
    Map mapResult=[:]
    mapResult += foo.replaceAll('\\[|\\]', '').split(',').collectEntries { entry ->
        def pair = entry.split(':')
        [(pair.first().trim()): pair.last().trim()]
       }

【讨论】:

    【解决方案2】:

    不完全是原生的 groovy,但对于序列化为 JSON 很有用:

    import groovy.json.JsonBuilder
    import groovy.json.JsonSlurper
    
    def map = ['a':2,'b':4 ]
    def s = new JsonBuilder(map).toString()
    println s
    
    assert map == new JsonSlurper().parseText(s)
    

    使用元编程:

    import groovy.json.JsonBuilder
    import groovy.json.JsonSlurper
    
    Map.metaClass.toJson   = { new JsonBuilder(delegate).toString() }
    String.metaClass.toMap = { new JsonSlurper().parseText(delegate) }
    
    def map = ['a':2,'b':4 ]
    assert map.toJson() == '{"a":2,"b":4}'
    assert map.toJson().toMap() == map
    

    很遗憾,无法覆盖 toString() 方法...

    【讨论】:

    • 我想这是一个干净的解决方案......通过一点元编程,这个功能可以很容易地添加到 String 类......
    【解决方案3】:

    如果您不想使用 evaluate(),请改用:

    def stringMap = "['a':2,'b':4]"
    stringMap = stringMap.replaceAll('\\[|\\]','')
    def newMap = [:]
    stringMap.tokenize(',').each {
    kvTuple = it.tokenize(':')
    newMap[kvTuple[0]] = kvTuple[1]
    }
    println newMap
    

    【讨论】:

    • 这是一个相当有限的,因为它只适用于字符串。例如,在您的示例中,放入 newMap 的值将是字符串“2”和“4”,而不是数字 2 和 4。
    【解决方案4】:

    您可能想使用 evaluate 尝试一些场景,它可能会满足您的需求。

    def stringMap = "['a':2,'b':4]"
    def map = evaluate(stringMap)
    
    assert map.a == 2
    assert map.b == 4
    
    def stringMapNested = "['foo':'bar', baz:['alpha':'beta']]"
    def map2 = evaluate(stringMapNested)
    
    assert map2.foo == "bar"
    assert map2.baz.alpha == "beta"
    

    【讨论】:

    • 谢谢。我想这和我要找的很接近!
    • 哇。刚刚注意到该解决方案存在相当大的安全风险!如果数据是通过 URL 传递的,而我们只是对其执行评估(),那么一切都会发生!所以我想 JSON 解决方案会是更好的方法...
    • 您可以使用Eval 类,它是GroovyShell 之上的一个简单助手:def map = Eval.me("['a':2,'b':4]")
    • 我想自己解析这个,太棒了
    • 很棒的评论@ArturoHerrero!谢谢
    【解决方案5】:

    我认为您正在寻找 ConfigObject 和 ConfigSlurper 的组合。像这样的东西可以解决问题。

    def foo = new ConfigObject()
    foo.bar = [ 'a' : 2, 'b' : 4 ]
    
    // we need to serialize it
    new File( 'serialized.groovy' ).withWriter{ writer ->
      foo.writeTo( writer )
    }
    
    def config = new ConfigSlurper().parse(new File('serialized.groovy').toURL())    
    
    // highest level structure is a map ["bar":...], that's why we need one loop more
    config.each { _,v ->
        v.each {key, value -> println key + " " + value}
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-29
      • 2013-10-26
      • 1970-01-01
      • 2010-10-27
      • 2015-01-07
      • 2017-01-03
      • 2017-12-15
      相关资源
      最近更新 更多