5

I was writing code and I noticed some odd behavior in Groovy when I am dealing with XML and Maps. I thought about it and can't figure out why is it happening and should it that way.

I wrote sample code with 3 examples. Crucial difference between map1 & map3 is only on the following part:

Map1:

map1 << ["${it.name()}":it.value()]

Map3:

map3["${it.name()}"]=it.value()

Here is full code, you can copy-paste it into Groovy console:

def xml = '<xml><head>headHere</head><body>bodyHere</body></xml>'


Map map1 = [:]

def node = new XmlParser().parseText(xml) 

node.each {
      map1 << ["${it.name()}": it.value()]
} 

println map1
println map1["head"]

println ">>>>>>>>>>>>>>>>>>>>>>"



Map map2 = [:]

map2 << ["head":"headHere"]
map2 << ["body":"bodyHere"]

println map2
println map2["head"]

println "<<<<<<<<<<<<<<<<<<<<<<"



def xml2 = '<xml><head>headHere</head><body>bodyHere</body></xml>'    

Map map3 = [:]

def node2 = new XmlParser().parseText(xml2) 

node2.each {
      map3["${it.name()}"]=it.value()
} 

println map3
println map3["head"]

The result that I am getting is following:

[head:[headHere], body:[bodyHere]]
null

[head:headHere, body:bodyHere]
headHere

[head:[headHere], body:[bodyHere]]
[headHere]

Even thou map1 and map3 look the same, the result of map["head"] is totally different, first gives null and second gives the actual result. I don't understand why is it happening. I spent some time on it and still don't get it. I used .getProperty() to get info on a class, but it looks the same and feels the same on both maps and object inside. I tried couple more things and nothing gives me any idea on what is happening. I even tried different OS (Win XP, Mac OS) and still nothing.

I don't have any ideas anymore, please can one some explain odd behavior, why is it happening and what is the difference between map << [key:object] and map[key] = object?

Thank you.

OverZealous
  • 39,252
  • 15
  • 98
  • 100
MeIr
  • 7,236
  • 6
  • 47
  • 80
  • 1
    There's a section on the [Strings and GStrings documentation](http://groovy.codehaus.org/Strings+and+GString) page for Groovy that warns about this. (see: ___GStrings aren't Strings___ about 2/3 of the way down) – tim_yates Sep 29 '11 at 08:38

2 Answers2

12

One thing that might help is, don't use GStrings for your keys. Groovy supports using objects directly as keys by wrapping them in parentheses.

From the manual:

Map keys are strings by default: [a:1] is equivalent to ["a":1]. But if you really want a variable to become the key, you have to wrap it between parentheses: [(a):1].

Fully working example:

def xml = '<xml><head>headHere</head><body>bodyHere</body></xml>'

Map map1 = [:]
def node = new XmlParser().parseText(xml)
node.each {
    map1 << [ (it.name()): it.value() ]
}

println map1
println map1["head"]
println ">>>>>>>>>>>>>>>>>>>>>>"

Map map2 = [:]

map2 << ["head":"headHere"]
map2 << ["body":"bodyHere"]

println map2
println map2["head"]

println "<<<<<<<<<<<<<<<<<<<<<<"

def xml2 = '<xml><head>headHere</head><body>bodyHere</body></xml>'

Map map3 = [:]

def node2 = new XmlParser().parseText(xml2)

node2.each {
    map3[it.name()] = it.value()
}

println map3
println map3["head"]

The output is:

[head:[headHere], body:[bodyHere]]
[headHere]
>>>>>>>>>>>>>>>>>>>>>>
[head:headHere, body:bodyHere]
headHere
<<<<<<<<<<<<<<<<<<<<<<
[head:[headHere], body:[bodyHere]]
[headHere]
OverZealous
  • 39,252
  • 15
  • 98
  • 100
2

Here is a demonstration of this quirk of double quoted strings in Groovy:

Double quoted strings are plain java.lang.String if there’s no interpolated expression, but are groovy.lang.GString instances if interpolation is present.

groovy:000> m = [:]
===> {}
groovy:000> tmp = "wat"
===> wat
groovy:000> key = "${tmp}"
===> wat
groovy:000> m << ["${key}": "hi"]
===> {wat=hi}
groovy:000> m["${key}"] = "hi"
===> hi
groovy:000> m
===> {wat=hi, wat=hi}
groovy:000> m["wat"] = "fuuuuuu!"
===> fuuuuuu!
groovy:000> m
===> {wat=hi, wat=fuuuuuu!}
groovy:000> m.keySet().each { println it.class }
class org.codehaus.groovy.runtime.GStringImpl
class java.lang.String

Enjoy ;)

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
  • Look at the classes of the keys. – Dave Newton Sep 29 '11 at 00:53
  • I understand that I am looking at different objects and therefore the reason I get null in my code. But what I don't understand is why in one case it is GString and in another is String. More over, if value for the key is GString or anything else - fine. But key seems to be a bit odd... I mean there is no limitation on what kind of class keys is, but if I expect to have all key as strings and I get GString - well that's gonna lead to some bug/issues. – MeIr Sep 29 '11 at 01:16