Groovy - with closure with multiple references - grails

I'm trying to parse a JSON data and assign it to a POJO in Grails.
I started with
obj.param=jsonRequest.jsonWrap.attrib.something.jsonParam
After some experimenting and refactoring, it looks like this now.
jsonRequest.jsonWrap.attrib.something.with {
obj.param1=jsonParam1
obj.param2=jsonParam2
//...
}
}
Now, can I avoid the repeated use of obj reference?

I'm imagining that your actual starting point is something like the following. On the JSON side:
import groovy.json.JsonSlurper
String jsonText = '''{
"jsonWrap":{
"attrib":{
"something":{
"jsonParam1": "value1",
"jsonParam2": "value2",
"jsonParam3": "value3",
"jsonParam4": "value4",
"jsonParam5": "value5"
}
}
}
}'''
def jsonRequest = new JsonSlurper().parseText(jsonText)
On the Groovy side:
class ObjectType {
def param1, param2, param3, param4, param5
}
def obj = new ObjectType()
Now, if I had any control over how either the JSON side or the Groovy side are defined then I would do my darnedest to ensure that the property names of the JSON "something" object are exactly the same as the property names in the Groovy "ObjectType" class. For example, like this:
class ObjectType {
def jsonParam1, jsonParam2, jsonParam3, jsonParam4, jsonParam5
}
Then, unmarshalling the "something" object into Groovy is as simple as this:
def obj = new ObjectType(jsonRequest.jsonWrap.attrib.something)
Only one reference to the JSON object. Only one reference to the Groovy object. And the former is used to instantiate the latter. And furthermore, notice that there is no need to reference the properties at all. That is, JSON objects from the slurper are instances of Map, so if the property names match up, you can use the default "Map constructor" syntax.
If, however, you do not control property naming in either set of objects, I would still recommend a different Map-based short-cut. First define a constant Map from one set of property names to the other, like so:
def map = [param1:"jsonParam1", param2:"jsonParam2", param3:"jsonParam3",
param4:"jsonParam4", param5:"jsonParam5"]
Then I would use something like this for the object unmarshalling:
def obj = new ObjectType().with { o ->
jsonRequest.jsonWrap.attrib.something.with { j ->
map.each { oParam, jParam -> o[oParam] = j[jParam] }
}
o
}

i don't think there is a trivial way to trick groovy into "use objectA, if getting is needed and objectB for setting". If obj above is a map or you can apply a map to this object, then you could produce a map in your with block and use this. If you have to have nested structures then more work is needed.
def jsonParam = new Expando([ p1: 'p1', p2: 'p2', p3: 'p3', ])
def obj = new Expando(
jsonParam.with{
[
param1: p1,
param3: p3,
] // `with` will return this map
})
assert obj.param1==jsonParam.p1
assert obj.param3==jsonParam.p3
I use expandos for simple code.

Related

Groovy Equivalent: javascript object methods

In JavaScript you can do the following:
var obj = {
property: 1,
method1: function() {
//...
},
method2: function() {
//...
}
};
obj.method1()
I am wondering if there is a groovy equivalent for this (a map containing a method). I know this is just like a class, but I dont want a class ha..
Yes, you can put closures inside a map. But this is not the way to get
objects in Groovy. There is no concept of "this", that knows about the
map.
def obj = [
inc: { it + 1 }
]
println obj.inc(10)
Ok so Javascript is not OOP. They have OBJECTS but that is it. What you are showing is an OBJECT.
In Groovy, you can do this with a class that can instantiate the object and then you can do that on the object. For example you can create a CommandObject (which is what you are probably wanting) and then fill in the properties like you want or fill them in on instantiation. For example (using above example):
def paramsDesc = new ParamsDescriptor()
paramsDesc.paramType = 'paramtype'
paramsDesc.keyType = 'keyType'
paramsDesc.name = 'name'
paramsDesc.idReferences = 'id'
paramsDesc.description = 'desc'
paramsDesc.mockData = 'mock'
paramsDesc.values = []
OR (if you create a constructor) you can instantiate all at once:
def paramsDesc = new ParamsDescriptor('paramtype','keyType','name','id','desc','mock',[])
CommandObjects can have methods and functions (like above). But you just have to instantiate them first (def paramsDesc = new ParamsDescriptor())
This is the difference between a class and an object; think of a class as the blueprint and the object as what is created from the blueprint.

How can I map properties conditionally with MapStruct 1.2?

Is it possible with MapStruct 1.2 to map a source property with a specific value to a specific different value in the target?
I think about something like this:
public abstract class JiraKpmMapper {
#Mappings({
#Mapping(source = "mySource.propA", target = "myTarget.propX")
})
#ValueMappings({
#ValueMapping(source = "ABC", target = "XYZ"),
#ValueMapping(source = "123", target = "789")
})
public abstract MyTarget source2Target(final MySource mySource);
}
So that when MapStruct sees during the mapping that when mySource.propA has the value "ABC" myTarget.propX needs to be set to value "XYZ" and so forth.
To be more precisely, I even want something more elaborated:
The target should be a class haven three properties in which the resulting target value must be split into.
For instance, if mySource.propA has the value "ABC" the target myTarget should get a value like "V01.123.456.AB". This value in turn shall be split up in a preValue, a middleValue and an endValue:
preValue = "V01"
middleValue = "123.456"
endValue = "AB"
whereby there's no property holding the complete result string.
That's why I already wrote a custom mapper and I tell the MyMapper to use it via
#Mapper(componentModel = "spring", uses = MyCustomMapper.class)
This works so far but I can't achieve it to tell the MyCustomMapper to put "V01.123.456.AB" into the target when the souzrce comes with "ABC".
You can't really do that with MapStruct. The #ValueMapping annotation is for mapping of Enum(s).
In order to achieve what you are looking for you would need to do that in #BeforeMapping or #AfterMapping.
For example you can do something like:
#Mapper
public interface JiraKpmMapper {
#BeforeMapping
default void beforeMapping(#MappingTarget MyTarget target, MySource source) {
if (source.getPropY().equals("ABC") {
target.setPropX("V01.123.456.AB");
}
}
#Mapping(target = "propX", ignore = true) // This is now mapped in beforeMapping
MyTarget source2Target(final MySource mySource);
}
Your custom mapper then should have an #AfterMapping. Where you would convert the propX into your class. You can even do this as part of the #BeforeMapping I wrote and directly create your class (or invoke the method that does the conversion from String into a class)
you can do something like this
#Mapping(target = "myTarget.propX",expression="java(mySource.getPropA().equals(\"Abc\")?"\"V01.123.456.AB\":\"\")")

Groovy: Dynamic nested properties [duplicate]

I am wondering if I can pass variable to be evaluated as String inside gstring evaluation.
simplest example will be some thing like
def var ='person.lName'
def value = "${var}"
println(value)
I am looking to get output the value of lastName in the person instance. As a last resort I can use reflection, but wondering there should be some thing simpler in groovy, that I am not aware of.
Can you try:
def var = Eval.me( 'new Date()' )
In place of the first line in your example.
The Eval class is documented here
edit
I am guessing (from your updated question) that you have a person variable, and then people are passing in a String like person.lName , and you want to return the lName property of that class?
Can you try something like this using GroovyShell?
// Assuming we have a Person class
class Person {
String fName
String lName
}
// And a variable 'person' stored in the binding of the script
person = new Person( fName:'tim', lName:'yates' )
// And given a command string to execute
def commandString = 'person.lName'
GroovyShell shell = new GroovyShell( binding )
def result = shell.evaluate( commandString )
Or this, using direct string parsing and property access
// Assuming we have a Person class
class Person {
String fName
String lName
}
// And a variable 'person' stored in the binding of the script
person = new Person( fName:'tim', lName:'yates' )
// And given a command string to execute
def commandString = 'person.lName'
// Split the command string into a list based on '.', and inject starting with null
def result = commandString.split( /\./ ).inject( null ) { curr, prop ->
// if curr is null, then return the property from the binding
// Otherwise try to get the given property from the curr object
curr?."$prop" ?: binding[ prop ]
}

Is there any way to return a Map with some fields from a createCriteria in Grails?

I have an object where I need to query and return it as a Map, but I don't want to iterate over the object again as I'm already doing a query and it can get a bad performance.
List<MyObject> myObjectList = MyObject.createCriteria().list(params) {
...
}
But what I need is a Map like:
myObjectList.each { MyObjectList myObject ->
myMap.add([person: myObject.person, myObject: myObject, listIntoObject: myObject.listInsideObject as List])
}
I found another way to get a map like this but it still not the better way to get a Map from a List that I'm querying with GORM...
myMap = myObjectList.collect{ [person: it.person, myObject: it, listIntoObject : it.invoiceDetails as List] }
Is there a better way to get a Map using GORM?
You can use org.hibernate.transform.Transformers to transform the result to an entity map
import org.hibernate.transform.Transformers
def criteria = MyObject.createCriteria()
criteria.resultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
def myObjectList = criteria.list(params) {
...
}
You can also fall back to HQL as well. Refer the UPDATE section of a similar answer for more details.
You can use import org.hibernate.criterion.CriteriaSpecification with withCriteria() to transform the result in a Collection object, with properties chosen by you only:
import org.hibernate.criterion.CriteriaSpecification
def criteria = User.withCriteria(){
resultTransformer CriteriaSpecification.ALIAS_TO_ENTITY_MAP
createAlias 'personalData', 'p'
eq 'id', params.id
// ...and so on, then
projections {
property 'id', 'myId' // a property called 'myId', from User
property 'p.name', 'userName' // from personalData, a property mapped by a fk in User
property 'p.age', 'age'
}
}

Grails data binding to command object with maps - convert key to number

I have a Grails command object with a list of maps. The map key is intended to be a numeric domain object ID.
class MyCommand {
def grid = [].withDefault { [:] }
}
Data binding to the list/map is working in general because of the dynamic list expansion.
However, in the POST, the map keys are being bound as Strings and I want them to be Longs, as they are when the form is initially populated. I want foo[123] in my map, not foo['123'].
Alternatively I would be satisfied if the [] operators found the correct value given a numeric ID key to look up. In other words, if I could get foo[123] to return the same value as foo['123'], that would work too.
Any way to get this to work the way I want to? Maybe strongly type the map?
Or a better approach?
You can inject the property into the map to convert a String key to Long. For example:
def myMap = [:] << ['1': "name"] << ['Test': "bobo"]
def result = myMap.inject([:]){map, v ->
def newKey = v.key.isNumber() ? v.key.toLong() : v.key
map[newKey] = v.value
map
}
assert myMap['1'] == 'name'
assert result[1L] == 'name'
assert result['Test'] == 'bobo'

Resources