rdmueller
rdmueller

Reputation: 11052

Groovy: isn't there a stringToMap out of the box?

as a tcl developer starting with groovy, I am a little bit surprised about the list and map support in groovy. Maybe I am missing something here.

I am used to convert between strings, lists and arrays/maps in tcl on the fly. In tcl, something like

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

would be possible, where as in groovy, the each command steps through each character of the string.

This would be much of a problem is I could easily use something like the split or tokenize command, but because a serialized list or map isn't just "a:2,b:4", it is a little bit harder to parse.

It seems that griffon developers use a stringToMap library (http://code.google.com/p/stringtomap/) but the example can't cope with the serialized maps either.

So my question is now: what's the best way to parse a map or a list in groovy?

Cheers, Ralf

PS: it's a groovy question, but I've tagged it with grails, because I need this functionality for grails where I would like to pass maps through the URL

Update: This is still an open question for me... so here are some updates for those who have the same problem:

Upvotes: 31

Views: 39826

Answers (5)

Ali
Ali

Reputation: 469

I hope this help:

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

Upvotes: 2

Jonas Eicher
Jonas Eicher

Reputation: 1511

Not exactly native groovy, but useful for serializing to 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)

with meta-programming:

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

unfortunately, it's not possible to override the toString() method...

Upvotes: 21

Noam Manos
Noam Manos

Reputation: 16990

If you don't want to use evaluate(), do instead:

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

Upvotes: 1

John Wagenleitner
John Wagenleitner

Reputation: 11035

You might want to try a few of your scenarios using evaluate, it might do what you are looking for.

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"

Upvotes: 33

Grega Kešpret
Grega Kešpret

Reputation: 12117

I think you are looking for a combination of ConfigObject and ConfigSlurper. Something like this would do the trick.

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}
}

Upvotes: 4

Related Questions