I'm looking for a way to avoid having to check for the property first with hasProperty().
Ideally I would like to have something like
def a = myobj.getPropertyOrElse("mypropname", "defaultvalueifpropertymissing')
I see that gradle has a findProperty() but I can't find a similar thing for plain groovy.
The hasProperty method returns a MetaProperty instance that you can use to retrieve the value by passing the original instance:
def a = myobj.hasProperty('mypropname')?.getProperty(myobj) ?:
'defaultvalueifpropertymissing'
And then use the Safe navigation operator(?.) and Elvis operator (?:) to avoid the if/else.
The shortest version I could think of is
def a = if (a.hasProperty("mypropname")) a.getProperty("mypropname") else "defaultvalueifmissing"
which obviously repeats the property name twice. Creating your own method is possible but it's limited to your current class.
class MyClass {
String name = "the name"
}
def a = new MyClass()
def getProperty(Object theInstance, String propName, Object defaultValue) {
if (theInstance.hasProperty(propName)) theInstance.getProperty(propName) else defaultValue
}
assert "the name" == getProperty(a, "name", "")
assert "default value" == getProperty(a, "method", "default value")
One can use getProperties() of MetaClass or getProperty() of GroovyObject:
class Test {
String field1
String field2
}
def test = new Test(field1: "value1", field2: null)
// using MetaClass.getProperties()
println test.properties["field1"] // value1
println test.properties["field2"] // null
println "field2" in test.properties.keySet() // true
println test.properties["field3"] // null
println "field3" in test.properties.keySet() // false
// using GroovyObject.getProperty()
println test.getProperty("field1") // value1
println test.getProperty("field2") // null
println test.getProperty("field3") // groovy.lang.MissingPropertyException
Related
I've been experimenting with closures and the delegate scope. I've been sucessful in changing the delegate for "regular closures" but not when converting a method to a closure via the .& operator:
class Mother {
def method2() { nonexisting }
Closure method3 = {-> nonexisting}
}
Mother julia = new Mother()
// works for regular closures
def c3 = julia.method3
c3.delegate = [nonexisting: 'nonexisting']
assert 'nonexisting' == c3.call() // 'nonexisting' comes from the delegate Map
// fails for method reference closure
def c2 = julia.&method2
c2.delegate == [nonexisting: 'nonexisting']
println "this = ${c2.thisObject}"
println "owner = ${c2.owner}"
println "delegate = ${c2.delegate}"
assert 'nonexisting' == c2.call() // raises MissingPropertyException
Am I doing it wrong or it's just not possible to make the method reference closure to lookup free variables in my an object of my choice?
I have JSON looking like:
{
"days": [
{
"mintemp": "21.8"
}
]
}
With Groovy, I parse it like this:
class WeatherRow {
String mintemp
}
def file = new File("data.json")
def slurper = new JsonSlurper().parse(file)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.mintemp
But actually, I would like to name my instance variable something like minTemp (or even something completely random, like numberOfPonies). Is there a way in Groovy to map a member of a map passed to a constructor to something else?
To clarify, I was looking for something along the lines of #XmlElement(name="mintemp"), but could not easily find it:
class WeatherRow {
#Element(name="mintemp")
String minTemp
}
Create a constructor that takes a map.
Runnable example:
import groovy.json.JsonSlurper
def testJsonStr = '''
{"days": [
{ "mintemp": "21.8" }
]}'''
class WeatherRow {
String minTemp
WeatherRow(map) {
println "Got called with constructor that takes a map: $map"
minTemp = map.mintemp
}
}
def slurper = new JsonSlurper().parseText(testJsonStr)
def days = slurper.days
def firstRow = days[0] as WeatherRow
println firstRow.minTemp
Result:
Got called with constructor that takes a map: [mintemp:21.8]
21.8
(of course you'd remove the println line, it's just there for the demo)
You can achieve this using annotation and simple custom annotation processor like this:
1. Create a Custom Annotation Class
#Retention(RetentionPolicy.RUNTIME)
#interface JsonDeserializer {
String[] names() default []
}
2. Annotate your instance fields with the custom annotation
class WeatherRow{
#JsonDeserializer(names = ["mintemp"])
String mintemp;
#JsonDeserializer(names = ["mintemp"])
String minTemp;
#JsonDeserializer(names = ["mintemp"])
String numberOfPonies;
}
3. Add custom json deserializer method using annotation processing:
static WeatherRow fromJson(def jsonObject){
WeatherRow weatherRow = new WeatherRow();
try{
weatherRow = new WeatherRow(jsonObject);
}catch(MissingPropertyException ex){
//swallow missing property exception.
}
WeatherRow.class.getDeclaredFields().each{
def jsonDeserializer = it.getDeclaredAnnotations()?.find{it.annotationType() == JsonDeserializer}
def fieldNames = [];
fieldNames << it.name;
if(jsonDeserializer){
fieldNames.addAll(jsonDeserializer.names());
fieldNames.each{i ->
if(jsonObject."$i")//TODO: if field type is not String type custom parsing here.
weatherRow."${it.name}" = jsonObject."$i";
}
}
};
return weatherRow;
}
Example:
def testJsonStr = '''
{
"days": [
{
"mintemp": "21.8"
}
]
}'''
def parsedWeatherRows = new JsonSlurper().parseText(testJsonStr);
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).mintemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).minTemp == "21.8"
assert WeatherRow.fromJson(parsedWeatherRows.days[0]).numberOfPonies == "21.8"
Check the full working code at groovyConsole.
I am having trouble understanding how the GroovyClassLoader cache works with expando Methods.
In particular, I expected the following code to give a different output for each call to Number.test(), yet this is not the case.
int i = 1
Number.metaClass.test = {
return "A string"
}
println i.test()
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
Number.metaClass = null
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
Number.metaClass.test = {
return "Another String"
}
println i.test()
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
Number.metaClass.test = {
return "Yet another String"
}
println i.test()
Output:
A string
groovy.lang.ExpandoMetaClass#3571b748[class java.lang.Number]
groovy.lang.MetaClassImpl#7748410a[class java.lang.Number]
A string
groovy.lang.ExpandoMetaClass#37f1104d[class java.lang.Number]
groovy.lang.MetaClassImpl#55740540[class java.lang.Number]
A string
Edit:
I tried some more things. It looks like my system is acting quite strangely. Given the following code:
def i = 1
i.class
Number.metaClass.test = {
return "A string"
}
println i.test()
Number.metaClass = null
Number.metaClass.test = {
return "Another String"
}
println i.test()
GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
Number.metaClass.test = {
return "Yet another String"
}
println i.test()
When I execute it on my system, it works as expected (getting 3 different strings in output) but it fails (3 x A string) if I comment the supposedly useless i.class.
However, if I execute it on groovy-console.appspot.com, it gives me the expected output with or without that line...
I'm kind of at a loss.
When you created the int i = 9, it created an object with the current Number.metaClass. It accepts creating a new method (read Number.metaClass.test = { "A string" }), but won't allow overload already existing ones. You can change the object's metaclass instead:
int i = 1
Number.metaClass.test = { "A string" }
assert i.test() == "A string"
Number.metaClass = null
i.metaClass.test = { "Another String" }
assert i.test() == "Another String"
GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
assert i.test() == "Another String"
i.metaClass.test = { "Yet another String" }
assert i.test() == "Yet another String"
Needing to invoke i.class looks like a bug to me... Taking a look at jira I saw some unresolved and opened bugs. I thought yours looked similar to these ones:
Overriding methods on a Java class metaClass doesn't take effect until instance metaClass is changed: https://issues.apache.org/jira/browse/GROOVY-5065
Using metaClass to override methods in class hierarchy does not work as expected: https://issues.apache.org/jira/browse/GROOVY-3942
Overriding methods via .metaClass doesn't behave consistently: https://issues.apache.org/jira/browse/GROOVY-6847
Removing a metaClass method: https://issues.apache.org/jira/browse/GROOVY-4189
The first part of my answer works if you consider a concrete class:
class Echo {}
Echo.metaClass.test = { "A string" }
def i = new Echo()
assert i.test() == "A string"
Echo.metaClass = null
Echo.metaClass.test = { "Another String" }
assert i.test() == "A string"
i.metaClass.test = { "Another String" }
assert i.test() == "Another String"
i.metaClass.test = { "Yet another String" }
assert i.test() == "Yet another String"
Using GStrings one can access the properties of the object, including nested properties. But how to access the n'th element inside a list property?
class Foo {
List<Bar> elements
}
class Bar {
String version
}
I need to access version property in Foo.elements object for a specific index using GString.
Tried below code without success.
def property = "elements[0].version"
fooObject."$property" fails to identify the property
So there are three ways in which I think this problem can be solved depending upon how much flexibility is allowed
class Foo {
List<Bar> elements
}
class Bar {
String version
}
Let's say fooObject is the object of Foo, e.g.:
def fooObject = new Foo(elements:[new Bar(version:1), new Bar(version:2)])
If this is possible for you:
println fooObject."elements"[1]."version"
Otherwise, put everything in a string and then interpolate:
println "${fooObject.elements[1].version}"
Ultimately, if both of the above don't fly for you:
def property='elements[1].version'
def expr = 'fooObject.' + property
println Eval.me('fooObject', fooObject, expr)
The last one makes the fooObject available as fooObject to the expression being evaluated and evaluates the expression.
Ideally, it could be:
def prop1 = "elements"
def prop2 = "version"
fooObject."$prop1"[0]."$prop2"
Lengthy and generic one would be using inject:
class Foo {
List<Bar> elements
}
class Bar {
String version
}
def fooObject = new Foo(elements: [new Bar(version: '1'),
new Bar(version: '2'),
new Bar(version: '3')])
def fetchVersion(property, fooObject) {
property.tokenize(/./).inject(fooObject) {obj, elem ->
if(elem.contains(/[/)){
def var = elem.tokenize(/[]/)
obj?."${var[0]}".getAt("${var[1]}".toInteger())
} else {
obj?."$elem"
}
}
}
assert fetchVersion("elements[0].version", fooObject) == '1'
assert fetchVersion("elements[1].version", fooObject) == '2'
assert fetchVersion("elements[2].version", fooObject) == '3'
assert fetchVersion("elements[7].version", fooObject) == null
the sample progame when i try to run using the expandometaclass technique it give me two output one the desired result second one "null" as output, from where null is picked up ?
class testA {
static def X(def var) {
Y(var)
}
static def Y(def var) {
println var
}
}
testA.metaClass.static.newMethod = {z_var -> X(z_var) }
println testA.newMethod("anish")
output:
anish
**null**
why this progranme also print null as output
The null is the return value from newMethod. In case you don't want this to be printed remove the println from your line
println testA.newMethod("anish")