Scala case class
インスタンス、例.
case class MyClass(param1: String, param2: String)
val x = MyClass("hello", "world")
ある種のマッピング、例えば.
getCCParams(x) returns "param1" -> "hello", "param2" -> "world"
これは、事前定義されたクラスだけでなく、どのケースクラスでも機能します。基になるProductクラスに問い合わせるメソッドを記述することで、ケースクラス名を引き出すことができることがわかりました。
def getCCName(caseobj: Product) = caseobj.productPrefix
getCCName(x) returns "MyClass"
だから私は同様のソリューションを探していますが、ケースクラスのフィールドを探しています。ソリューションはJavaリフレクションを使用する必要があるかもしれませんが、将来のScalaケースクラスの基礎となる実装が変更されます。
現在、私はScala=サーバーで作業し、ケースクラスを使用してプロトコルとそのすべてのメッセージと例外を定義しています。これらは非常に美しく簡潔な構造であるためです。それらをJavaマップに入れて、使用するクライアント実装のメッセージングレイヤーを介して送信します。現在の実装では、各ケースクラスの翻訳を個別に定義していますが、一般化されたソリューション。
これは動作するはずです:
def getCCParams(cc: AnyRef) =
(Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
ケースクラスは Product を拡張するため、単に.productIterator
フィールド値を取得するには:
def getCCParams(cc: Product) = cc.getClass.getDeclaredFields.map( _.getName ) // all field names
.Zip( cc.productIterator.to ).toMap // zipped with all values
または、代わりに:
def getCCParams(cc: Product) = {
val values = cc.productIterator
cc.getClass.getDeclaredFields.map( _.getName -> values.next ).toMap
}
製品の利点の1つは、値を読み取るためにフィールドでsetAccessible
を呼び出す必要がないことです。もう1つは、productIteratorがリフレクションを使用しないことです。
この例は、他のクラスを拡張せず、コンストラクターの外部でフィールドを宣言しない単純なケースクラスで機能することに注意してください。
誰かが再帰バージョンを探している場合、@ Andrejsのソリューションの修正を次に示します。
def getCCParams(cc: Product): Map[String, Any] = {
val values = cc.productIterator
cc.getClass.getDeclaredFields.map {
_.getName -> (values.next() match {
case p: Product if p.productArity > 0 => getCCParams(p)
case x => x
})
}.toMap
}
また、ネストされたケースクラスを、ネストの任意のレベルでマップに展開します。
形のないものを使用できます。
させて
case class X(a: Boolean, b: String,c:Int)
case class Y(a: String, b: String)
LabelledGeneric表現を定義する
import shapeless._
import shapeless.ops.product._
import shapeless.syntax.std.product._
object X {
implicit val lgenX = LabelledGeneric[X]
}
object Y {
implicit val lgenY = LabelledGeneric[Y]
}
2つのタイプクラスを定義して、toMapメソッドを提供します
object ToMapImplicits {
implicit class ToMapOps[A <: Product](val a: A)
extends AnyVal {
def mkMapAny(implicit toMap: ToMap.Aux[A, Symbol, Any]): Map[String, Any] =
a.toMap[Symbol, Any]
.map { case (k: Symbol, v) => k.name -> v }
}
implicit class ToMapOps2[A <: Product](val a: A)
extends AnyVal {
def mkMapString(implicit toMap: ToMap.Aux[A, Symbol, Any]): Map[String, String] =
a.toMap[Symbol, Any]
.map { case (k: Symbol, v) => k.name -> v.toString }
}
}
その後、このように使用できます。
object Run extends App {
import ToMapImplicits._
val x: X = X(true, "bike",26)
val y: Y = Y("first", "second")
val anyMapX: Map[String, Any] = x.mkMapAny
val anyMapY: Map[String, Any] = y.mkMapAny
println("anyMapX = " + anyMapX)
println("anyMapY = " + anyMapY)
val stringMapX: Map[String, String] = x.mkMapString
val stringMapY: Map[String, String] = y.mkMapString
println("anyMapX = " + anyMapX)
println("anyMapY = " + anyMapY)
}
印刷する
anyMapX = Map(c-> 26、b-> bike、a-> true)
anyMapY = Map(b-> 2番目、a->最初)
stringMapX = Map(c-> 26、b-> bike、a-> true)
stringMapY = Map(b-> 2番目、a->最初)
ネストされたケースクラスの場合、(ネストされたマップ) 別の回答
汎用関数にする必要がない場合の簡単なバリエーションを次に示します。
case class Person(name:String, age:Int)
def personToMap(person: Person): Map[String, Any] = {
val fieldNames = person.getClass.getDeclaredFields.map(_.getName)
val vals = Person.unapply(person).get.productIterator.toSeq
fieldNames.Zip(vals).toMap
}
scala> println(personToMap(Person("Tom", 50)))
res02: scala.collection.immutable.Map[String,Any] = Map(name -> Tom, age -> 50)
Scala 2.13
、case class
es( Product
の実装として)の開始には、フィールドのイテレータを返す productElementNames メソッドが提供されます。名前。
productIterator で取得したフィールド値でフィールド名を圧縮することにより、関連するMap
を一般的に取得できます。
// case class MyClass(param1: String, param2: String)
// val x = MyClass("hello", "world")
(x.productElementNames Zip x.productIterator).toMap
// Map[String,Any] = Map("param1" -> "hello", "param2" -> "world")
インタープリターパッケージのProductCompletion
を使用したソリューション:
import tools.nsc.interpreter.ProductCompletion
def getCCParams(cc: Product) = {
val pc = new ProductCompletion(cc)
pc.caseNames.Zip(pc.caseFields).toMap
}
たまたまJson4sを使用している場合は、次のことができます。
import org.json4s.{Extraction, _}
case class MyClass(param1: String, param2: String)
val x = MyClass("hello", "world")
Extraction.decompose(x)(DefaultFormats).values.asInstanceOf[Map[String,String]]
私はニースについては知りません...しかし、これは少なくともこの非常に基本的な例ではうまくいくようです。おそらくいくつかの作業が必要ですが、開始するには十分でしょうか?基本的に、ケースクラス(または他のクラス:/)からすべての「既知の」メソッドを除外します。
object CaseMappingTest {
case class MyCase(a: String, b: Int)
def caseClassToMap(obj: AnyRef) = {
val c = obj.getClass
val predefined = List("$tag", "productArity", "productPrefix", "hashCode",
"toString")
val casemethods = c.getMethods.toList.filter{
n =>
(n.getParameterTypes.size == 0) &&
(n.getDeclaringClass == c) &&
(! predefined.exists(_ == n.getName))
}
val values = casemethods.map(_.invoke(obj, null))
casemethods.map(_.getName).Zip(values).foldLeft(Map[String, Any]())(_+_)
}
def main(args: Array[String]) {
println(caseClassToMap(MyCase("foo", 1)))
// prints: Map(a -> foo, b -> 1)
}
}
commons.mapper.Mappers.Mappers.beanToMap(caseClassBean)