map constructor for List<CustomObject> ?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
plg
Reply | Threaded
Open this post in threaded view
|

map constructor for List<CustomObject> ?

plg
Hi all,

I'm doing a little YAML-parsing where I have CustomObjects having Lists
of other CustomObjects as properties, but the default map constructor
does not seem to work for these lists.
example:

class Sample {
   Integer age
   String name
}
class SampleCollection {
   String name
   List<Sample> listOfSamples
}
def m = [name:'Collection1',listOfSamples:[
[age:10,name:'Anna'],[age:12,name:'Berta'] ]]
def c = m as SampleCollection // or c = new SampleCollection(m)
assert c.class == SampleCollection // passes
assert c.listOfSamples[0].class == Sample // fails

First solution I've come up with is adding a custom constructor to
SampleCollection like this:

   SampleCollection(Map map) {
     name = map.name
//     listOfSamples = map.listOfSamples // cast not working
     listOfSamples = []
     map.listOfSamples.each{ sample ->
       listOfSamples << (sample as Sample)
     }
   }

This works, but seems rather inefficient, as
a) I have to loop through each list with potentially many items and
b) SampleCollection has some more simple properties like 'name', which I
don't want to add all manually. Is there a way to use the default for
these simple properties?


regards
Paul
--
typed with Neo 2 -- an ergonomically optimized keyboard layout
for German, English, programming, and science

http://neo-layout.org/
Reply | Threaded
Open this post in threaded view
|

Re: map constructor for List<CustomObject> ?

Anthony Hepple
Hi Paul

If you provide just a setter for listOfSamples, instead of a map constructor that handles all properties, then you can allow Groovy's dynamic map construction process to handle all the other properties. Something like this...

class Sample {
  Integer age
  String name
}
class SampleCollection {
  String name
  List<Sample> listOfSamples = []

  def setListOfSamples (List samples) {
    samples.each { sample ->
       listOfSamples << (sample as Sample)
    }
  }
}
def m = [name:'Collection1',listOfSamples:[ [age:10,name:'Anna'],[age:12,name:'Berta'] ]]
def c = m as SampleCollection // or c = new SampleCollection(m)
assert c.class == SampleCollection // passes
assert c.listOfSamples[0].class == Sample // NOW PASSES
assert c.listOfSamples[0].name.class == String
assert c.listOfSamples[0].age.class == Integer


On 10 November 2017 at 11:04, paul <[hidden email]> wrote:
Hi all,

I'm doing a little YAML-parsing where I have CustomObjects having Lists of other CustomObjects as properties, but the default map constructor does not seem to work for these lists.
example:

class Sample {
  Integer age
  String name
}
class SampleCollection {
  String name
  List<Sample> listOfSamples
}
def m = [name:'Collection1',listOfSamples:[ [age:10,name:'Anna'],[age:12,name:'Berta'] ]]
def c = m as SampleCollection // or c = new SampleCollection(m)
assert c.class == SampleCollection // passes
assert c.listOfSamples[0].class == Sample // fails

First solution I've come up with is adding a custom constructor to SampleCollection like this:

  SampleCollection(Map map) {
    name = map.name
//     listOfSamples = map.listOfSamples // cast not working
    listOfSamples = []
    map.listOfSamples.each{ sample ->
      listOfSamples << (sample as Sample)
    }
  }

This works, but seems rather inefficient, as
a) I have to loop through each list with potentially many items and
b) SampleCollection has some more simple properties like 'name', which I don't want to add all manually. Is there a way to use the default for these simple properties?


regards
Paul
--
typed with Neo 2 -- an ergonomically optimized keyboard layout
for German, English, programming, and science

http://neo-layout.org/



--
Anthony Hepple
01704 227828 / 07931 504049
http://www.dhdevelopment.co.uk
plg
Reply | Threaded
Open this post in threaded view
|

Re: map constructor for List<CustomObject> ?

plg
Am 11.11.2017 um 17:55 schrieb Anthony Hepple:
> If you provide just a setter for listOfSamples, instead of a map
> constructor that handles all properties, then you can allow Groovy's
> dynamic map construction process to handle all the other properties.
Thanks Anthony,

guess that was just too simple and obvious for me to notice :D

Nevertheless I'd be interested in WHY the list-entries are not cast
automagically. Does Groovy even check the <T> type of list elements, or
just ignores it?
If someone can give me some insights, that would be much appreciated.

thanks in advance,
Paul

> On 10 November 2017 at 11:04, paul <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     Hi all,
>
>     I'm doing a little YAML-parsing where I have CustomObjects having
>     Lists of other CustomObjects as properties, but the default map
>     constructor does not seem to work for these lists.
>     example:
>
>     class Sample {
>        Integer age
>        String name
>     }
>     class SampleCollection {
>        String name
>        List<Sample> listOfSamples
>     }
>     def m = [name:'Collection1',listOfSamples:[
>     [age:10,name:'Anna'],[age:12,name:'Berta'] ]]
>     def c = m as SampleCollection // or c = new SampleCollection(m)
>     assert c.class == SampleCollection // passes
>     assert c.listOfSamples[0].class == Sample // fails
>
>     First solution I've come up with is adding a custom constructor to
>     SampleCollection like this:
>
>        SampleCollection(Map map) {
>          name = map.name <http://map.name>
>     //     listOfSamples = map.listOfSamples // cast not working
>          listOfSamples = []
>          map.listOfSamples.each{ sample ->
>            listOfSamples << (sample as Sample)
>          }
>        }
>
>     This works, but seems rather inefficient, as
>     a) I have to loop through each list with potentially many items and
>     b) SampleCollection has some more simple properties like 'name',
>     which I don't want to add all manually. Is there a way to use the
>     default for these simple properties?

--
typed with Neo 2 -- an ergonomically optimized keyboard layout
for German, English, programming, and science

http://neo-layout.org/
Reply | Threaded
Open this post in threaded view
|

Re: map constructor for List<CustomObject> ?

Anthony Hepple

Nevertheless I'd be interested in WHY the list-entries are not cast automagically. Does Groovy even check the <T> type of list elements, or just ignores it?
If someone can give me some insights, that would be much appreciated.

As far as I understand it, Groovy ignores all generic type declarations. Generics are syntactically valid Groovy only to support copy/paste of Java code. 
Automagic casting would involve recursive assignment of properties during groovy's map construction process. Instead the map construction process calls the default constructor followed by the setters indicated by the map's keys.

def c = new SampleCollection(name: 'collection1', listOfSamples: [[age: 10, name: 'Anna'],...])  

Is executed as;

GroovyObject c = new SampleCollection();
c.setName('collection1');
c.setListOfSamples([[age: 10, name: 'Anna'],...]);

[[age: 10, name: 'Anna'],...] is a list literal it is assigned (as an ArrayList object) to the listOfSamples property.
[age: 10, name: 'Anna'] is a map literal which becomes a LinkedHashMap object and at no time (until you introduce the custom setter) is it cast to a Sample object.

Unless overridden with a custom setter, the generated setter is a simple assignment of argument to property. Eg. ArrayList -> List

Hope this helps.