316
316

Reputation: 11

How do I instantiate a lazy list in Grails?

I am trying to accept a JSON array into a command object. The array looks like this:

[
{prop1:'a', 'prop2:'b', prop3:'c'},
{prop1:'d', 'prop2:'e', prop3:'f'},
{prop1:'g', 'prop2:'h', prop3:'i'},
...
]

I was trying to accept the array via a lazy list

class MyCommand {
    List myList = ListUtils.lazyList([], FactoryUtils.instantiateFactory(Map))
}

but I get the following error:

InstantiateFactory: The constructor must exist and be public . Stacktrace follows:
Message: InstantiateFactory: The constructor must exist and be public
    Line | Method
->>  113 | findConstructor    in org.apache.commons.collections.functors.InstantiateFactory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|     86 | <init>             in     ''
|     67 | getInstance . . .  in     ''
|    121 | instantiateFactory in org.apache.commons.collections.FactoryUtils

What is the correct syntax to accept an array of maps?

I am sending the data via remoteFunction like so:

var params = '{"myList":' + JSON.stringify(myList) + '}';
${remoteFunction(controller: 'myController', action: 'myAction', 
    params: 'params', onSuccess: 'callback(data)')};

Is there a different method I should use?

Upvotes: 0

Views: 535

Answers (2)

Jeff Scott Brown
Jeff Scott Brown

Reputation: 27220

You don't need to use a lazy list in your command object. You can simplify your code and let the built in data binding system initialize the List lazily.

// grails-app/controllers/demo/DemoController.groovy
package demo

class DemoController {

    def demo(MyCommand co) {
        [command: co]
    }
}

class MyCommand {
    List<Map> data
}

You can send a request to that action with JSON that looks like this...

{"data": [
{prop1:'a', 'prop2:'b', prop3:'c'},
{prop1:'d', 'prop2:'e', prop3:'f'},
{prop1:'g', 'prop2:'h', prop3:'i'},
...
]}

When you do that the data binding system will create an instance of MyCommand, it will initialize the data property initially to be an empty List and then will populate that List with all of the Maps represented in the JSON.

That code is in the project at https://github.com/jeffbrown/listofmaps. That project includes the following test which passes:

 // test/unit/demo/DemoControllerSpec.groovy
package demo

import grails.test.mixin.TestFor
import spock.lang.Specification


@TestFor(DemoController)
class DemoControllerSpec extends Specification {

    void "test binding a List of Map to a command object"() {
        when:
        request.method = 'POST'
        request.json = '''
            {"data": [
                      {"prop1":"a", "prop2":"b", "prop3":"c"},
                      {"prop1":"d", "prop2":"e", "prop3":"f"},
                      {"prop1":"g", "prop2":"h", "prop3":"i"}
                     ]
            }'''
        def model = controller.demo()
        def command = model.command

        then:
        command instanceof MyCommand
        command.data instanceof List
        command.data.size() == 3

        command.data[0] instanceof Map
        command.data[0].prop1 == 'a'
        command.data[0].prop2 == 'b'
        command.data[0].prop3 == 'c'

        command.data[1] instanceof Map
        command.data[1].prop1 == 'd'
        command.data[1].prop2 == 'e'
        command.data[1].prop3 == 'f'

        command.data[2] instanceof Map
        command.data[2].prop1 == 'g'
        command.data[2].prop2 == 'h'
        command.data[2].prop3 == 'i'
    }
}

Upvotes: 1

doelleri
doelleri

Reputation: 19682

Map is an interface, so you cannot instantiate it. You need to pick a concrete type to use, like HashMap or LinkedHashMap.

Upvotes: 0

Related Questions