I am new to functional programming paradigm and hoping to learn the concepts using groovy. I have a json text containing a list of several person objects like the following:
{
"persons":[
{
"id":1234,
"lastname":"Smith",
"firstname":"John"
},
{
"id":1235,
"lastname":"Lee",
"firstname":"Tommy"
}
]
}
What I am trying to do store them in list or array of Person groovy class like the following:
class Person {
def id
String lastname
String firstname
}
I would like to do this using a closure. I tried something like:
def personsListJson= new JsonSlurper().parseText(personJsonText) //personJsonText is raw json string
persons = personsListJson.collect{
new Person(
id:it.id, firstname:it.firstname, lastname:it.lastname)
}
This didn't work. Does collect operations supposed to behave this way? If so then how do I write it?
Try
personsListJson.persons.collect {
new Person( id:it.id, firstname:it.firstname, lastname:it.lastname )
}
And as there is a 1:1 mapping between the json and the constructor parameters, you can simplify that to:
personsListJson.persons.collect {
new Person( it )
}
But I'd keep the first method, as if the Json got an extra value in it (maybe out of your control) then the second method would break
You can try it-
List<JSON> personsListJson = JSON.parse(personJsonText);
persons = personsListJson.collect{
new Person(id:it.id, firstname:it.firstname, lastname:it.lastname)
}
Related
I have list of domain objects which each one of them need to be called as follows:
(<DOMAIN CLASS>.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
})
The idea is to have this code once, while changing the code a given parameter.
I know that there are several ways to implement it using groovy, and I tried to use them all.
I need to know what is the best practice and short way to do this.
Thanks!
You said you have a List of domain classes so the code below assumes that is true. You don't say what you want to do with the results of each of those queries, so I will assume you have that under control.
You could do something like this...
def listOfDomainClasses = // you have initialized this list somehow...
listOfDomainClasses.each { domainClass ->
def resultForThisClass = domainClass.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
})
// do something with resultForThisClass
}
I hope that helps.
I'm assuming you are using Grails, since you tagged this question with Gorm. If so, try this:
Class clazz = grailsApplication.domainClasses.find { it.clazz.simpleName == "<DOMAINCLASS>" }.clazz
clazz.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
}
Or replace grailsApplication.domainClasses and use your list of domain classes instead.
It is not clear what you are really trying to do but maybe what you want is to write a method like this...
/**
* #param someDomainClass A domain class
* #return the results of the query
*/
def myQueryMethod(Class someDomainClass) {
someDomainClass.withCriteria {
dataSecurityGroups {
'in' 'id', entitiesIds as Long[]
}
}
}
Then you can call that method and pass as an argument whatever domain class is appropriate.
Is that the sort of thing you are looking for?
I have this code:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
'identity'() {
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}
}
return writer.toString();
}
It produces this xml:
<catalog xmlns='http://www.sybrium.com/XMLSchema/NodeCatalog'>
<groupId>sample.group</groupId>
<artifactId>sample-artifact</artifactId>
<version>1.0.0</version>
</catalog>
Notice that the "identity" tag is missing... I've tried everything in the world to get that node to appear. I'm ripping my hair out!
Thanks in advance.
There might be a better way, but one trick is to call invokeMethod directly:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
delegate.invokeMethod('identity', [{
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}])
}
return writer.toString();
}
This is effectively what Groovy is doing behind the scenes. I couldn't get delegate.identity or owner.identity to work, which are the usual tricks.
Edit: I figured out what's going on.
Groovy adds a method with a signature of identity(Closure c) to every object.
This means that when you tried to dynamically invoke the identity element on the XML builder, while passing in a single closure argument, it was calling the identity() method, which is like calling delegate({...}) on the outer closure.
Using the invokeMethod trick forces Groovy to bypass the Meta Object Protocol and treat the method as a dynamic method, even though the identity method already exists on the MetaObject.
Knowing this, we can put together a better, more legible solution. All we have to do is change the signature of the method, like so:
String buildCatalog(Catalog catalog) {
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.catalog(xmlns:'http://www.sybrium.com/XMLSchema/NodeCatalog') {
// NOTE: LEAVE the empty map here to prevent calling the identity method!
identity([:]) {
groupId(catalog.groupId)
artifactId(catalog.artifactId)
version(catalog.version)
}
}
return writer.toString();
}
This is much more readable, it's clearer the intent, and the comment should (hopefully) prevent anyone from removing the "unnecessary" empty map.
I want to implement an 'active' flag for rules in my DSL. Here's how I wanted it to look like:
Shipping("Standard") {
active: true
description: "some text"
rules {
... define rules here
}
}
Here's how I got everything running following several tutorials:
Script dslScript = new GroovyShell().parse(new File("Standard"))
dslScript.metaClass.Shipping = { String name, Closure cl ->
ShippingDelegate delegate = new ShippingDelegate()
delegate.name = name
cl.delegate = delegate
cl.setResolveStrategy Closure.DELEGATE_FIRST
cl()
}
dslScript.run()
ShippingDelegate is simple:
class ShippingDelegate {
String name
void rules(Closure cl) {
... do stuff here
}
}
It all runs fine without complaints but how can I access 'active' or 'description'?
What does this syntax actually do anyway? It looks like a map assignment, but there is none. Or does the groovy compiler treat it as an incomplete ternary operator?
May I suggest a small change in your DSL so that your design can be simplified?
Edited, it is not clear in you example if you have more than one shipping instance. In my second try, I assume that the answer is yes
class ShippingRules {
boolean active
String description
String name
ShippingRules(String name) {
this.name=name
}
def rules(Closure c) {
c.delegate=this
c()
}
}
abstract class ShippingRulesScript extends Script {
def shipppingRules =[]
def shipping(String name, Closure c) {
def newRules=new ShippingRules(name)
shipppingRules << newRules
c.delegate=newRules
c()
}
}
def cfg= new CompilerConfiguration(
scriptBaseClass:ShippingRulesScript.name
)
Script dslScript = new GroovyShell(cfg).parse(new File("Standard"))
dslScript.run()
The DSL should be changed to this:
shipping("Standard") {
active= true
description= "some text"
rules {
... define rules here
}
}
shipping("International") {
active= true
description= "some text"
rules {
... define rules here
}
}
I.e. lose the capital to shipping, and use assignments instead of colons.
You would then later be able to retrieve the shipping rules from your dslScript shippingRules variable.
disclaimer: I cannot test my code right now, so there may be some typos in the code, but you get the general idea: use a base class where you provide your rules and properties to your script.
I asked a similar question on Google+, see here.
The summary is: you can use the map syntax only on constructors (ctors) and as function parameters.
Interesting is that it doesn't throw an exception.
I'm wondering what is the best way to retrieve nested properties in Groovy, taking a given Object and arbitrary "property" String. I would like to something like this:
someGroovyObject.getProperty("property1.property2")
I've had a hard time finding an example of others wanting to do this, so maybe I'm not understanding some basic Groovy concept. It seems like there must be some elegant way to do this.
As reference, there is a feature in Wicket that is exactly what I'm looking for, called the PropertyResolver:
http://wicket.apache.org/apidocs/1.4/org/apache/wicket/util/lang/PropertyResolver.html
Any hints would be appreciated!
I don't know if Groovy has a built-in way to do this, but here are 2 solutions. Run this code in the Groovy Console to test it.
def getProperty(object, String property) {
property.tokenize('.').inject object, {obj, prop ->
obj[prop]
}
}
// Define some classes to use in the test
class Name {
String first
String second
}
class Person {
Name name
}
// Create an object to use in the test
Person person = new Person(name: new Name(first: 'Joe', second: 'Bloggs'))
// Run the test
assert 'Joe' == getProperty(person, 'name.first')
/////////////////////////////////////////
// Alternative Implementation
/////////////////////////////////////////
def evalProperty(object, String property) {
Eval.x(object, 'x.' + property)
}
// Test the alternative implementation
assert 'Bloggs' == evalProperty(person, 'name.second')
Groovy Beans let you access fields directly. You do not have to define getter/setter methods. They get generated for you. Whenever you access a bean property the getter/setter method is called internally. You can bypass this behavior by using the .# operator. See the following example:
class Person {
String name
Address address
List<Account> accounts = []
}
class Address {
String street
Integer zip
}
class Account {
String bankName
Long balance
}
def person = new Person(name: 'Richardson Heights', address: new Address(street: 'Baker Street', zip: 22222))
person.accounts << new Account(bankName: 'BOA', balance: 450)
person.accounts << new Account(bankName: 'CitiBank', balance: 300)
If you are not dealing with collections you can simply just call the field you want to access.
assert 'Richardson Heights' == person.name
assert 'Baker Street' == person.address.street
assert 22222 == person.address.zip
If you want to access a field within a collection you have to select the element:
assert 'BOA' == person.accounts[0].bankName
assert 300 == person.accounts[1].balance
You can also use propertyMissing. This is what you might call Groovy's built-in method.
Declare this in your class:
def propertyMissing(String name) {
if (name.contains(".")) {
def (String propertyname, String subproperty) = name.tokenize(".")
if (this.hasProperty(propertyname) && this."$propertyname".hasProperty(subproperty)) {
return this."$propertyname"."$subproperty"
}
}
}
Then refer to your properties as desired:
def properties = "property1.property2"
assert someGroovyObject."$properties" == someValue
This is automatically recursive, and you don't have to explicitly call a method. This is only a getter, but you can define a second version with parameters to make a setter as well.
The downside is that, as far as I can tell, you can only define one version of propertyMissing, so you have to decide if dynamic path navigation is what you want to use it for.
See
https://stackoverflow.com/a/15632027/2015517
It uses ${} syntax that can be used as part of GString
I've built a custom builder in Groovy by extending BuilderSupport. It works well when configured like nearly every builder code sample out there:
def builder = new MyBuilder()
builder.foo {
"Some Entry" (property1:value1, property2: value2)
}
This, of course, works perfectly. The problem is that I don't want the information I'm building to be in the code. I want to have this information in a file somewhere that is read in and built into objects by the builder. I cannot figure out how to do this.
I can't even make this work by moving the simple entry around in the code.
This works:
def textClosure = { "Some Entry" (property1:value1, property2: value2) }
builder.foo(textClosure)
because textClosure is a closure.
If I do this:
def text = '"Some Entry" (property1:value1, property2: value2)'
def textClosure = { text }
builder.foo(textClosure)
the builder only gets called for the "foo" node. I've tried many variants of this, including passing the text block directly into the builder without wrapping it in a closure. They all yield the same result.
Is there some way I take a piece of arbitrary text and pass it into my builder so that it will be able to correctly parse and build it?
Your problem is that a String is not Groovy code. The way ConfigSlurper handles this is to compile the text into an instance of Script using GroovyClassLoader#parseClass. e.g.,
// create a Binding subclass that delegates to the builder
class MyBinding extends Binding {
def builder
Object getVariable(String name) {
return { Object... args -> builder.invokeMethod(name,args) }
}
}
// parse the script and run it against the builder
new File("foo.groovy").withInputStream { input ->
Script s = new GroovyClassLoader().parseClass(input).newInstance()
s.binding = new MyBinding(builder:builder)
s.run()
}
The subclass of Binding simply returns a closure for all variables that delegates the call to the builder. So assuming foo.groovy contains:
foo {
"Some Entry" (property1:value1, property2: value2)
}
It would be equivalent to your code above.
I think the problem you described is better solved with a slurper or parser.
See:
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper
http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlParser
for XML based examples.
In your case. Given the XML file:
<foo>
<entry name='Some Entry' property1="value1" property2="value2"/>
</foo>
You could slurp it with:
def text = new File("test.xml").text
def foo = new XmlSlurper().parseText(text)
def allEntries = foo.entry
allEntries.each {
println it.#name
println it.#property1
println it.#property2
}
Originally, I wanted to be able to specify
"Some Entry" (property1:value1, property2: value2)
in an external file. I'm specifically trying to avoid XML and XML-like syntax to make these files easier for regular users to create and modify. My current solution uses ConfigSlurper and the file now looks like:
"Some Entry"
{
property1 = value1
property2 = value2
}
ConfigSlurper gives me a map like this:
["Some Entry":[property1:value1,property2:value2]]
It's pretty simple to use these values to create my objects, especially since I can just pass the property/value map into the constructor.