Groovy - 有效地将CSV数据映射到对象的构造函数中

时间:2012-04-10 14:35:35

标签: java grails csv groovy

我有一个由像这样的复杂对象组成的对象

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),而无需进行太多解析

3 个答案:

答案 0 :(得分: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'

希望它有所帮助。 : - )

答案 1 :(得分: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),而只是从中反序列化。

答案 2 :(得分:0)

我正在回答我的问题。到目前为止,我发现的最简单的方法是将标题列映射到类中的封装数据成员。例如:考虑Main类具有ObjectB(名称为roll)作为数据成员。然后在CSV / XLS文件中,您可以将标题列命名为roll.number。在解析过程中将文件行转换为映射时,我们可以直接将此映射传递给构造函数,并相应地分配所有值,即所有复杂的子对象都将使用文件中定义的值进行初始化。

我已经实现了这种技术,它就像一个魅力。