2016-08-12 10 views
2

実行時にjsonファイルを読み込んでケースクラスを作成できるスカラープログラムを書くためのヒントが必要です。例として、私たちのようなJSONクラスがある場合 -スカラのjsonを構文解析するメタプログラミング

Employ{ 
    name:{datatype:String, null:false} 
    age:{datatype:Int, null:true} 
    Address:{city: {datatype: String, null:true}, zip: {datatype: String, null:false}} 
} 

をして、これはScalaでそれを行うことが可能であろう

case class Employ(name: String, age: Option[Int], address: Address} 
case class Address(city: Option[String], zip:String} 

のようなクラスを作成する必要がありますか?

+0

したがって、実行時にクラスを動的に定義したいとします。あなたはスカラでそれを行うことができますが、スカラ反射に対して非常に良い保持をした後でなければなりません。私のアドバイス - 絶対に必要でない限り、このアプローチは避けてください。 –

+0

そして、あなたがこれを達成することができたとしても、これらのクラスとそのインスタンスを使用するには、再度スカラリフレクションをよく理解する必要があります。 –

+0

このプロジェクトでは、問題の一部(https://github.com/julianpeeters/case-class-generator)を解決しようとしています。あなたは、このプロジェクトのすべてのコードが英語のように簡単に見えるようになってから問題を解決できると自ら考えることができます。 –

答えて

2

はい、これはTreeHuggerを使用すると簡単に達成できます。私は私の仕事のプロジェクトのために同様のことをしました。

以下は、Scala Akka Actorクラスを生成するおもちゃの例です。

基本的に
import argonaut.Argonaut._ 
import argonaut._ 
import org.scalatest.FunSuite 
import treehugger.forest._ 
import definitions._ 
import treehuggerDSL._ 

class ConvertJSONToScalaSpec extends FunSuite { 

    test("read json") { 

    val input = 
     """ 
     |{ 
     | "rulename" : "Rule 1", 
     | "condition" : [ 
     | { 
     |  "attribute" : "country", 
     |  "operator" : "eq", 
     |  "value" : "DE" 
     | } 
     | ], 
     | "consequence" : "route 1" 
     |} 
     """.stripMargin 

    val updatedJson: Option[Json] = input.parseOption 

    val tree = 
     BLOCK(
     IMPORT(sym.actorImports), 
     CLASSDEF(sym.c).withParents(sym.d, sym.e) := 
      BLOCK(
      IMPORT(sym.consignorImport, "_"), 
      DEFINFER(sym.methodName) withFlags (Flags.OVERRIDE) := BLOCK(
       CASE(sym.f DOT sym.methodCall APPLY (REF(sym.mc))) ==> 
       BLOCK(
        sym.log DOT sym.logmethod APPLY (LIT(sym.logmessage)), 
        (IF (sym.declaration DOT sym.header DOT sym.consignor DOT sym.consignoreTID ANY_== LIT(1)) 
        THEN (sym.sender APPLY() INFIX ("!", LIT(sym.okcm))) 
        ELSE 
        (sym.sender APPLY() INFIX ("!", LIT(sym.badcm))) 
        ) 
       ) 
      ) 
     ) 
    ) inPackage (sym.packageName)  
} 

あなたがする必要があるすべてはTreeHuggerマクロを使用する方法を考え出すです;:それはうまくいけば、あなたのアイデアを取得するには、クリーンアップする必要がありますが、各マクロはScalaの特定のキーワードを表します。これは、メタプログラミングを行う型安全な方法を提供します。

Scala Metaもありますが、私はそれを使用していません。

+0

彼の要求は、これらのマクロがコンパイル時に機能するため、Scalaマクロを使用して解決することはできません。彼が必要とするのは、実行時に利用可能なJSON構造に合わせて新しいクラスを実行時に定義する能力です。そして実行時に役立つ唯一のものは反映です。 –

+0

はい、彼はそれを達成することができます。 JSONは、TreeHuggerを使用して解析され、クラスファイルに変換されます(コンパイラライブラリがアクセス可能であることを確認する必要があります)。彼がテキスト表現を持つと、彼はそれを動的にコンパイルし、クラスローダを使って生成されたクラスをロードすることができます。 –

+0

あなたがtreehuggerが実行時に未知のクラスを生成してインスタンス化するのを助けることができると思えば、あなたはtreehuggerについて間違った考えを持っていると思います。 Treehuggerは実行時に 'scala-code strings'を作成するのに役立ちます。あなたの唯一の希望は、何らかの形でランタイムからインタープリタを取得し、何とか 'code-string'を解釈するためにそれを使用することですが、それでも動作する可能性は非常に低いです。 –

2

まあ... treehuggerまたはscala metaなどのライブラリを使用して、ケースクラスのコード文字列を生成するとします。今すぐあなたが取ることができる複数のアプローチがあります。そのうちの1つで始めるには、次のことができます。

// import the current runtime mirror as cm 
import scala.reflect.runtime.{currentMirror => cm} 

// you case code string 
val codeString = """ 
    case class Address(city: Option[String], zip:String) 

    Address(Some("CityName"), "zipcode") 
""" 

// get the toolbox from mirror 
val tb = cm.mkToolBox() 

// use tool box to convert string to Tree 
val codeTree = tb.parse(codeString) 

// eval your tree 
val address = tb.eval(codeTree) 

問題はval addressはタイプAnyを持っているということです。また、宇宙はまだタイプAddressについて知らないので、address.asInstanceOf[Address]を行うことはできません。

ClassSymbolClassLoaderについて調べることでこれを解決できます。スカラとJavaでリフレクションがどのように機能するかを理解することで、多くの問題を解決できるかもしれません。しかし、それは高い努力と成功の道を保証するものではありません。

+0

私はそれも好きです。ここにあなたのビットです:) – tesnik03