2012-04-10 10 views
0

Iは、要件ファイルからCSVデータをロードし、上記のインスタンスに割り当てることで、このグルービー - 効率的なオブジェクトのコンストラクタにCSVデータをマッピング

class ObjectA { 
    int cool 
    Object1 b 
    Object2 b 
} 


class Object1 { 
    int go 
    String do 
} 

のような複雑なオブジェクトで構成されているオブジェクトを持っていますオブジェクト。私はGrails CSVプラグインを使用しており、ファイルからCSVデータを取得できます。各行は、固有のオブジェクト・インスタンスの値を含むMAPです。マップは、次の形式である:

cool: 1, object1go: 3, object1do: 'hello', object2hm: 'world' 

私の質問はどのように私は効率的に多くの解析を行うことなく、ObjectAクラス内のデータメンバ(すなわちObject1)に「object1go」を渡すと「object1do」することができます。

+1

同じ名前のプロパティを2つ持つことはできません。 – ataylor

答えて

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のような中間ステップを検討して、その代わりに逆シリアル化することもできます。

+1

実際にObject1のインスタンスを明示的に作成する必要はありません。あなたのマップが正しい構造を持っている限り、Groovyはあなたのためにそれらを作成します。 –

+0

@JustinPiperああ、そこに行くと、確信が持てませんでしたので、私は冗長の面で間違っていました。/ –

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' 

はそれがお役に立てば幸いです。 :-)

+0

これは、私が考えていたクリーンアップのラインに沿っています –

0

私は私の質問に答えています。今まで私が見つけた最も簡単な方法は、ヘッダー列をクラスのカプセル化されたデータメンバーにマップすることです。例:メインクラスはデータメンバーとしてObjectB(名前のロール)を持つとします。 CSV/XLSファイルでは、ヘッダ列にroll.numberという名前を付けることができます。解析中にファイル行がマップに変換されると、このマップをコンストラクタに直接渡すことができ、すべての値が割り当てられます。つまり、すべての複雑な子オブジェクトはファイルに定義された値で初期化されます。

私はこのテクニックを実装しました。それは魅力的です。

関連する問題