用于解析scala中的json的元编程

时间:2016-08-12 20:34:38

标签: scala metaprogramming

我需要一些提示来编写一个可以读取json文件并在运行时创建一个case类的scala程序。作为一个例子,如果我们有像json类 -

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

这应该创建类似

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

是否可以在scala中执行此操作?

2 个答案:

答案 0 :(得分:3)

是的,您可以使用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,但我还没有用过它。

答案 1 :(得分:2)

嗯...假设您使用了treehuggerscala meta等其他库来生成案例类的代码字符串。现在您可以采取多种方法。要从其中一个开始,您可以执行以下操作。

// 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。此外,Universe仍然不知道类型Address,因此您将无法执行address.asInstanceOf[Address]

您可以通过探索有关ClassSymbolClassLoader的内容来解决这个问题,如果有足够的运气可以解决更多有关Scala和Java中的反射如何工作的问题。但这将是一项艰巨的努力,并不能保证成功之路。