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

我不知道

这在 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'

希望它有帮助。

(这是放在一起的;它可以大大增强/封装。

由于默认 ctor 采用映射,因此最简单的方法是通过吸掉嵌入对象的名称前缀来创建每个对象的必需参数映射。

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,然后从中反序列化。

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

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

最新更新