0

I'm doing some gross stuff, like using Groovy's metaClass and Eval to dynamically assign properties to an object from an XML import:

class ObjectBuilder {
  def assignProp(String propName, String propValue) {
    Eval.x(this, "x.metaClass.${propName} = '${propValue}'")     
  }
}

def h = new ObjectBuilder()
h.assignProp('name', 'Henson')
println(h.name)

What I'd like to do though, is be able instantiate another copy of the class inside itself:

Eval.x(this, "x.metaClass.${ObjName} = new ObjectBuilder()")

But I can't, because I think the class isn't passed to the binding. Is there another solution?

techHenson
  • 93
  • 1
  • 5
  • What are you trying to do? Can you give a little bit more context? You have a XML, you parse an object out of it and you try to add a dynamic property to it? Or it is a `Node` from `Xml{Parser|Builder}`? Why do you want to instantiate an `ObjectBuilder`? What is the main objective? – Will Oct 07 '14 at 17:23
  • Hey @WillP, I guess I'm learning more than anything ;-) What I'm doing is reading an XML, but I want to create an object based off it, using a specification that I pass in as a string (I only want a few things from the XML, and in a different structure.) This part's just where I'm trying to dynamically build the object. – techHenson Oct 07 '14 at 17:41

2 Answers2

2

A couple of solutions:

Expando

You may try working with a bunch of Expandos:

h = new Expando()
h.last = new Expando()
h.last.name = 'tech'

assert h.last.name == 'tech'

Metaclass the object directly

xml = '''
<person>
  <name>john</name>
  <surname>doe</surname>
  <age>41</age>
  <location>St Louis, MO</location>
</person>
'''

class Person {
  def name, surname, location, age
}

root = new XmlParser().parseText xml

person = new Person(root.children().collectEntries { [it.name(), it.text()] })

person.metaClass.getFullname = { "$delegate.name $delegate.surname" }

assert person.fullname == "john doe"

person.metaClass.likes = "chicken with lemon"

assert person.likes == "chicken with lemon" 

Maps

map = [:]
map.last = [:]
map.last.name = 'Tech'
assert map.last.name == 'Tech'
Will
  • 14,348
  • 1
  • 42
  • 44
  • Well, that's AMAZING! Have you seen any patterns if the XML is like.. `HensonWill`? I have some data that's a few levels deep, with arrays. – techHenson Oct 08 '14 at 18:40
  • @techHenson you should post your XML with that nested structure in your question – Will Oct 08 '14 at 19:12
1

Passing a newly instantiated object then assigning it with Eval seems to work:

class ObjectBuilder {
  def assignProp(String propName, String propValue) {
    Eval.x(this, "x.metaClass.${propName} = '${propValue}'")     
  }
  def nestObj(String objName) {
    Eval.xy(this, new ObjectBuilder(), "x.metaClass.${objName} = y")
  }
}

ObjectBuilder h = new ObjectBuilder()
h.assignProp('name', 'Henson')
h.nestObj('last')
h.last.assignProp('name', 'Tech')

println h.name + ' ' + h.last.name
techHenson
  • 93
  • 1
  • 5