[英]Groovy - Efficiently map CSV data into object's constructor
我有一個 object 由這樣的復雜對象組成
class ObjectA {
int cool
Object1 b
Object2 b
}
class Object1 {
int go
String do
}
要求是從文件加載 CSV 數據並將其分配給上述 object 的實例。我正在使用 Grails CSV 插件,我能夠從文件中檢索 CSV 數據。 每行都是一個 MAP,其中包含一個唯一的 object 實例的值。 map 格式如下:
cool: 1, object1go: 3, object1do: 'hello', object2hm: 'world'
我的問題是如何有效地將“ object1go
”和“ object1do
”傳遞給ObjectA
class 內的數據成員(即Object1
),而無需進行大量解析。
我不知道這在 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'
希望能幫助到你。 :-)
(這是一起扔的;它可以大大增強/封裝。)
由於默認的 ctors 使用映射,最簡單的方法是通過去除嵌入對象的名稱前綴來創建每個對象的必需參數 map。
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 更改 map 名稱,您可能會考慮一個中間步驟,例如 JSON,而只是反序列化。
我正在回答我的問題。 到目前為止,我發現的最簡單的方法是將 map 的 header 列封裝到 class 中的數據成員。例如:考慮 Main class 將ObjectB
(名稱為 roll)作為數據成員。 然后在 CSV/XLS 文件中,您可以將 header 列命名為roll.number
。 當文件行在解析過程中轉換為 map 時,我們可以直接將此 map 傳遞給構造函數,所有值都將相應地分配,即所有復雜的子對象都將使用文件中定義的值進行初始化。
我已經實施了這項技術,而且效果很好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.