[groovy-user] serialize maps with default as maps

213 views
Skip to first unread message

OC

unread,
Mar 21, 2013, 2:48:19 PM3/21/13
to user@groovy.codehaus.org User
Hello there,

I would need to serialize a pretty complex tree of maps withDefault into a file as plain maps (to read them later, when withDefault is not needed anymore, for further processing). The structure looks somewhat like this:

def map=[:].withDefault{[:].withDefault{[:].withDefault{[] as Set}}}

Alas, none of the "self-evident" object-oriented solutions

map.getMetaClass().writeReplace<<{ -> this as LinkedHashMap }

nor

map.getMetaClass().writeObject<<{ stream -> stream<<(this as LinkedHashMap) }

work, in both cases I'm still getting "java.io.NotSerializableException: groovy.lang.MapWithDefault" -- presumably the culprit being that the darned static Java thing does not see groovy metaclasses at all :(

Before I dive into boilerplate-heavy solutions like

(a) manually converting the whole structure in a number of recursively nested collects, or, more probably (for it's cleaner)
(b) defining my own serializable MapWithDefault subclass and overriding Map.withDefault so that it gets used instead the Groovy one...

... can someone see a groovier way to do that?

Thanks!
OC


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


OC

unread,
Mar 21, 2013, 5:10:19 PM3/21/13
to user@groovy.codehaus.org User
Well...

On Mar 21, 2013, at 7:48 PM, OC <o...@ocs.cz> wrote:

> (b) defining my own serializable MapWithDefault subclass and overriding Map.withDefault so that it gets used instead the Groovy one...

Primarily, let me please emphasize that "final" is pure evil. Nobody should _ever_ use the bloody thing! :( Due to that ugly and nonsense abomination, one can't subclass. Embedding's not the way out either, for it would cause heaps of boilerplace, since missedMethod redirection does not work in Java either.

Thus, after resorting to the ugliest solution of all possible ones, namely, to

Map.metaClass.asPlainMap<<{ collectEntries { k,v ->
if (v in Map) v=v.asPlainMap()
[k,v]
}}


I've bumped into one extremely weird behaviour which I can't understand for a sweet world. Check please this:

331 /tmp> <q.groovy
def map=[:].withDefault{[]}
map[1]=2
Map.metaClass.whatTheHeck<<{-> println ">> ${delegate.keySet()}" }
map.whatTheHeck()
332 /tmp> groovy q
>> [1, whatTheHeck]
333 /tmp>

is it normal? Is it the presumed result and am I just overlooking something obvious? Note please I don't ever use the property access ("map.whatTheHeck=..."), I am just calling a method... and it gets called all right. Just, as a side-effect, adds itself to the map keys?!? :-O

The above's important. The below is *not*, but nevertheless...

... due to the above thing which adds a String key to my map, I've also bumped into

groovy:000> [hi:'there',2:1].sort()
ERROR java.lang.ClassCastException:
java.lang.String cannot be cast to java.lang.Integer
at groovysh_evaluate.run (groovysh_evaluate:2)
...
groovy:000>

Darn. Is that the intended behaviour? I would rather expect it to be able to compare anything to anything, at the _very_ least numbers to strings. After all, e.g., "['2',1].sort()" works perfectly and as presumed.

Oh, and since I'm ranting anyway -- Groovy _definitely_ should allow proper variable scoping, like C (and any other decent structured language) does, i.e.,

2.times { val -> val.times { val -> println "$val" }}

should work, _exactly_ same way "2.times { it.times { println "$it" }}" does. This very-much-Java approach "we know you are a stupid clot and you would mess up your nested variables and thus we don't let you to nest them at all" is completely pathetic :( Due to the continuous need to rename nested variables whenever I copy/pasted a block to another place it took me twice more time to isolate the above weird thing than it would normally :(

When I get to writing jiras -- alas these things took lots of time I really had not, so won't be anytime soon -- I'm adding this there, too, as a feature request of the highest importance possible.

Thanks,

Cédric Champeau

unread,
Mar 22, 2013, 11:12:33 AM3/22/13
to us...@groovy.codehaus.org
This is an interesting finding... In case of a method missing, if the
receiver is a map, then Groovy checks if a property (entry with
key==method name) exists, and if so, returns the property. Here, it
checks that the map contains the key "whatTheHeck", but since it's a
MapWithDefault, the key doesn't exist and it generates an entry!

The faulty code is in MetaClassImpl:

if (object instanceof Map)
value = ((Map)object).get(methodName);

We should change this to:

if (object instanceof Map && ((Map)object).containsKey(methodName))
value = ((Map)object).get(methodName);

I wouldn't be surprised if they are multiple places in the code that
fall into this trap...

Le 21/03/2013 22:10, OC a �crit :
--
C�dric Champeau
SpringSource - A Division Of VMware
http://www.springsource.com/
http://twitter.com/CedricChampeau
Reply all
Reply to author
Forward
0 new messages