2

I have two nested case classes:

case class InnerClass(param1: String, param2: String)
case class OuterClass(myInt: Int, myInner: InnerClass)
val x = OuterClass(11, InnerClass("hello", "world"))

Which I want to convert to nested Maps of type Map[String,Any] so that I get something like this:

Map(myInt -> 11, myInner -> Map(param1 -> hello, param2 -> world))

Of course, the solution should be generic and work for any case class.

Note: This discussion gave a good answer on how to map a single case class to a Map. But I couldn't adapt it to nested case classes. Instead I get:

Map(myInt -> 11, myInner -> InnerClass(hello,world)
4

3 回答 3

2

正如 Luigi Plinge 在上面的评论中指出的那样,这是一个非常糟糕的主意——您将类型安全抛到了窗外,并且会遇到很多丑陋的强制转换和运行时错误。

也就是说,使用新的Scala 2.10 Reflection API很容易做你想做的事:

def anyToMap[A: scala.reflect.runtime.universe.TypeTag](a: A) = {
  import scala.reflect.runtime.universe._

  val mirror = runtimeMirror(a.getClass.getClassLoader)

  def a2m(x: Any, t: Type): Any = {
    val xm = mirror reflect x

    val members = t.declarations.collect {
      case acc: MethodSymbol if acc.isCaseAccessor =>
        acc.name.decoded -> a2m((xm reflectMethod acc)(), acc.typeSignature)
    }.toMap

    if (members.isEmpty) x else members
  }

  a2m(a, typeOf[A])
}

接着:

scala> println(anyToMap(x))
Map(myInt -> 11, myInner -> Map(param1 -> hello, param2 -> world))

但是,不要这样做。事实上,您应该尽最大努力避免在 Scala 中完全避免运行时反射——这实际上几乎从来没有必要。我只是发布这个答案,因为如果您决定必须使用运行时反射,那么使用 Scala 反射 API 比使用 Java 更好。

于 2013-03-10T17:40:45.900 回答
0

只需递归调用它。所以

def getCCParams(cc: AnyRef) =
  (Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
    f.setAccessible(true)
    val value = f.get(cc) match {
      // this covers tuples as well as case classes, so there may be a more specific way
      case caseClassInstance: Product => getCCParams(caseClassInstance)
      case x => x
    }
    a + (f.getName -> value)
  }
于 2013-03-10T17:33:00.727 回答
0

这是一个基于无形的更原则的解决方案。 https://github.com/yongjiaw/datacrafts

class NoSchemaTest extends FlatSpec with ShapelessProduct.Implicits {

"Marshalling and unmarshalling with Map" should "be successful" in {

val op = NoSchema.of[TestClass]

assert(
  op.operator.marshal(
    Map(
      "v1" -> 10,
      "v5" -> Map("_2" -> 12),
      "v3" -> Iterable(Seq("v21" -> 3)),
      "v6" -> TestClass3(v31 = 5)
    )) == TestClass(
    v1 = 10,
    v5 = (null, 12),
    v3 = Some(Seq(Some(
      TestClass2(
        v21 = 3,
        v22 = null
      )))),
    v6 = Some(TestClass3(v31 = 5)),
    v2 = None,
    v4 = null
  )
)

assert(
  op.operator.unmarshal(
    TestClass(
      v1 = 1,
      v2 = null
    )
  ) == Map(
    "v1" -> 1,
    "v2" -> null,
    // the rest are default values
    "v6" -> null,
    "v5" -> Map("_2" -> 2, "_1" -> "a"),
    "v4" -> null,
    "v3" -> Seq(
      Map(
        "v21" -> 3,
        "v22" -> Map("v" -> Map(), "v32" -> Seq(12.0), "v31" -> 0)
      )
    )
  )
)

 }
}

object NoSchemaTest {

case class TestClass(v1: Int,
v2: Option[Seq[Option[Double]]] = None,
v3: Option[Seq[Option[TestClass2]]] = Some(Seq(Some(TestClass2()))),
v4: Seq[Int] = null,
v5: (String, Int) = ("a", 2),
v6: Option[TestClass3] = None
)

case class TestClass2(v21: Int = 3,
v22: TestClass3 = TestClass3(0)
)

case class TestClass3(v31: Int,
v32: Iterable[Double] = Seq(12),
v: Map[String, Int] = Map.empty
)

}

trait DefaultRule extends Operation.Rule {

override def getOperator[V](operation: Operation[V]): Operation.Operator[V] = {

operation.context.noSchema match {

  case _: Primitive[V] => new PrimitiveOperator[V](operation)

  case shapeless: ShapelessProduct[V, _] =>
    new ShapelessProductMapper[V](operation, shapeless)

  case option: OptionContainer[_] =>
    new OptionOperator[option.Elem](
      option.element, operation.asInstanceOf[Operation[Option[option.Elem]]])
      .asInstanceOf[Operation.Operator[V]]

  case map: MapContainer[_] =>
    new MapOperator[map.Elem](
      map.element, operation.asInstanceOf[Operation[Map[String, map.Elem]]])
      .asInstanceOf[Operation.Operator[V]]

  case seq: SeqContainer[_] =>
    new SeqOperator[seq.Elem](
      seq.element, operation.asInstanceOf[Operation[Seq[seq.Elem]]])
      .asInstanceOf[Operation.Operator[V]]

  case iterable: IterableContainer[_] =>
    new IterableOperator[iterable.Elem](
      iterable.element, operation.asInstanceOf[Operation[Iterable[iterable.Elem]]])
      .asInstanceOf[Operation.Operator[V]]
}}}
于 2018-06-22T08:12:58.267 回答