0

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

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而无需进行大量解析。

4

3 回答 3

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'

希望能帮助到你。:-)

于 2012-04-10T21:11:59.697 回答
1

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

由于默认 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,并且只是从中反序列化。

于 2012-04-10T16:42:50.460 回答
0

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

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

于 2012-04-17T15:49:24.197 回答