Using Groovy metaClass to Implement Special Methods

544 Views Asked by At

I'm trying to modify the metaclass for JSONObject in Groovy to make it behave as much as possible like a regular Groovy map. When I implement methods in the metaclass, some of them are straightforward like JSONObject.metaClass.size in the example below. JSONObject has a length() method and I'm just wiring it up to a new size() method, but some methods have special meanings. For example, to get the subscript assignment to work, I had to override propertyMissing, not putAt. It looks like a lot of the collection operations like each, collect, findAll, etc are similar.

My first question is what special methods would I need to override in this case to make each() work? My second question is how would I figure out the answer myself? Is there a reference somewhere with methods that get special treatment from the MOP? I tried looking at the groovy-core source code, but there's a lot in there and I don't know where to start.

JSONObject.metaClass.propertyMissing = { String name, newValue -> delegate.put(name, newValue) }
JSONObject.metaClass.size = { -> delegate.length() }
JSONObject.metaClass.each = { cl -> delegate.keys().collectEntries{ [(it): delegate[it] ]}.each(cl) }

def json = new JSONObject()
json['a'] = 999
json.b    = 2.2
json['c'] = 'the letter C'
println json['a']            // Prints 999
println json['b']            // Prints 2.2
println json.c               // 'the letter C'
println json.size()         // Prints 3

//No signature of method: ... $__spock_feature_0_0_closure4.doCall() is applicable 
json.each{ k,v -> println "$k = $v"}
1

There are 1 best solutions below

1
daggett On BEST ANSWER
@Grab(group='org.json', module='json', version='20160810')

import org.json.JSONArray
import org.json.JSONObject

JSONObject.metaClass.each={Closure c-> 
    delegate.keys().each{ k-> c(k, delegate.get(k) ) }  
}

JSONObject.metaClass.setProperty={String k, Object v-> 
    delegate.put(k,v) 
}

JSONObject.metaClass.getProperty={String k-> 
    delegate.get(k) 
}

JSONObject.metaClass.size = { -> delegate.length() }

def json = new JSONObject()
json['a'] = 999
json.b    = 2.2
json['c'] = 'the letter C'
println json['a']            // Prints 999
println json['b']            // Prints 2.2
println json.c               // 'the letter C'
println json.size()         // Prints 3

//No signature of method: ... $__spock_feature_0_0_closure4.doCall() is applicable 
json.each{ k,v -> println "$k = $v"}

output:

999
2.2
the letter C
3
a = 999
b = 2.2
c = the letter C