2012-04-10 4 views
0

У меня есть объект, который состоит из сложных объектов, как этотGroovy - эффективно отобразить данные CSV в конструктор объекта

class ObjectA { 
    int cool 
    Object1 b 
    Object2 b 
} 


class Object1 { 
    int go 
    String do 
} 

Требования заключается в загрузке данных CSV из файла и присвоить его экземпляр выше объект. Я использую плагин Grails CSV, и я могу получить CSV-данные из файла. Каждая строка представляет собой MAP, содержащий значения для уникального экземпляра объекта. Карта находится в следующем формате:

cool: 1, object1go: 3, object1do: 'hello', object2hm: 'world' 

Мой вопрос, как я могу эффективно передать «object1go» и «object1do» данным членом (т.е. Object1) внутри ObjectA класса без необходимости делать много разбора.

+1

У вас не может быть двух свойств с тем же именем. – ataylor

ответ

1

(Это брошено вместе,. Он может быть значительно повышена/инкапсулированный)

Поскольку по умолчанию ctors взять карты, проще всего было бы создать необходимую карту параметров каждого объекта путем отсасывания имя префиксы встраиваемого объекта.

class Object1 { 
    int go 
    String s 
    String toString() { 
    "<<${super.toString()}: go=${go}, s=${s}>>" 
    } 
} 

class ObjectA { 
    int cool 
    Object1 b 
    String toString() { 
    "<<${super.toString()}: cool=${cool}, b=${b}>>" 
    } 
} 

params = [cool: 1, object1go: 3, object1s: 'hello'] 

// Params for embedded object. 
o1params = params.findAll { it.key.startsWith("object1") } 

// Embedded object's property names (the above map minus the prefix). 
tmp1 = o1params.collectEntries { k, v -> [(k[7..-1]): v] } 

// "Parent" object's params.  
oaparams = params - o1params 

oa = new ObjectA(oaparams + [b: new Object1(tmp1)]) 
println oa.toString() 

Существует несколько способов, которыми это может быть усилено, все они довольно легкие и прямые. Например, я жестко закодировал имя и длину "object1"; это может быть обернуто в универсальный метод, DSL и т. д. Имена свойств могут быть получены непосредственно из классов. Есть несколько способов сделать это чище.

Если вы можете изменить имена карт из CSV, вы можете рассмотреть промежуточный шаг, например JSON, и просто отделить от этого вместо этого.

+1

На самом деле вам не нужно явно создавать экземпляры Object1. Пока ваша карта имеет правильную структуру, Groovy также создаст их для вас. –

+0

@ JustinPiper Ах, вот вы и пришли - не уверен, поэтому я ошибся на стороне многословия:/ –

2

Я не знаю, насколько хорошо это будет работать в Grails, но это работает в Groovy:

class ObjectA { 
    String name 
    Object1 object1 
    Object2 object2 

    ObjectA(java.util.Map attrs) { 
    attrs.each { key, val -> 
     this.class.declaredFields.each { 
      if (!it.synthetic) { 
       def className = it.type.name.toLowerCase() 
       def localVar = it.name 
       if (key =~ /^${className}/) { 
        def realKey = key.replaceAll("^${className}", "") 

        if (!this."${localVar}") { 
         this."${localVar}" = Class.forName("${className.capitalize()}", true, this.class.classLoader).newInstance() 
        } 

        this."${localVar}"."${realKey}" = val.replaceAll("'", "") 
       } else { 
        try { 
         this."${key}" = val.replaceAll("'", "") 
        } catch (MissingPropertyException e) { } 
       } 
      } 
     } 
    } 
    } 
} 

class Object1 { 
    String foo 
    String bar 
} 

class Object2 { 
    String foo 
    String bar 
} 


def data = "name: 'dan', object1foo: 'food', object1bar: 'baz', object2foo: 'foor', object2bar: 'xanax'" 
def attrs = data.split(',').inject([:]) { map, keyPair -> 
    keyPair.split(':').with { map[it[0].trim()] = it[1].trim() } 
    map 
} 

def a = new ObjectA(attrs) 

assert a.name == 'dan' 
assert a.object1 instanceof Object1 
assert a.object2 instanceof Object2 
assert a.object1.foo == 'food' 
assert a.object2.foo == 'foor' 
assert a.object1.bar == 'baz' 
assert a.object2.bar == 'xanax' 

Надеется, что это помогает. :-)

+0

Да, это по линиям очистки, о которых я думал - + 1 –

0

Я отвечаю на свой вопрос. Самый простой способ, который я нашел до сих пор, - сопоставить столбец заголовка с инкапсулированными членами данных в классе. Например: Рассмотрим Основной класс имеет ObjectB (по имени roll) в качестве члена данных. Затем в файле CSV/XLS вы можете назвать столбец заголовка roll.number. Когда строка файла преобразуется в карту во время разбора, мы можем напрямую передать эту карту в конструктор, и все значения будут назначены соответственно, то есть все сложные дочерние объекты будут инициализированы значениями, определенными в вашем файле.

Я реализовал эту технику и работает как шарм.

Смежные вопросы