Groovy的 - 有效CSV数据映射到对象的构造
我有一个是由复杂的物体的像这样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
类,而不需要做太多的解析。
(这被放在一起;它可大大提高/封装的)
由于默认构建函数取的地图,最简单的是创建由吸出嵌入的对象的名称前缀每个对象的所需的参数映射。
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等中间步骤,然后从中反序列化。
实际上,您不需要明确创建Object1的实例。只要您的地图具有正确的结构,Groovy也会为您创建它们。 – 2012-04-10 19:28:22
@JustinPiper啊,你去那儿 - 不确定,所以我在冗长的一面犯了错误:/ – 2012-04-10 20:04:32
我不知道该怎么好,这将在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 – 2012-04-10 21:18:52
我在回答我的问题。到目前为止我发现的最简单的方法是将头列映射到类中的封装数据成员。例如:考虑Main类具有ObjectB
(通过名称卷)作为数据成员。然后在CSV/XLS文件中,您可以将标题列命名为roll.number
。当文件行在解析过程中转换为映射时,我们可以直接将此映射传递给构造函数,并相应地分配所有值,即所有复杂子对象都将使用文件中定义的值进行初始化。
我已经实现了这种技术,它的工作原理就像一个魅力。
您不能有两个具有相同名称的属性。 – ataylor 2012-04-10 15:08:25