문제

스칼라를 변환 할 수있는 좋은 방법이 있습니까? case class 예를 들어, 예를 들어

case class MyClass(param1: String, param2: String)
val x = MyClass("hello", "world")

어떤 종류의 매핑으로, 예를 들어

getCCParams(x) returns "param1" -> "hello", "param2" -> "world"

사전 정의 된 클래스가 아니라 모든 사례 클래스에서 작동합니다. 기본 제품 클래스를 심문하는 메소드를 작성하여 케이스 클래스 이름을 꺼낼 수 있음을 알았습니다.

def getCCName(caseobj: Product) = caseobj.productPrefix 
getCCName(x) returns "MyClass"

그래서 나는 비슷한 솔루션을 찾고 있지만 사례 클래스 필드를 찾고 있습니다. 솔루션이 Java Reflection을 사용해야 할 수도 있다고 생각하지만, 사례 클래스의 기본 구현이 변경되면 미래의 Scala 릴리스에서 깨질 수있는 무언가를 쓰는 것을 싫어합니다.

현재 스칼라 서버에서 작업하고 있으며 케이스 클래스를 사용하여 프로토콜과 모든 메시지 및 예외를 정의하고 있습니다. 그러나 클라이언트 구현을 위해 메시징 계층을 보내려면 Java지도로 변환해야합니다. 내 현재 구현은 각 사례 클래스의 번역을 별도로 정의하지만 일반화 된 솔루션을 찾는 것이 좋을 것입니다.

도움이 되었습니까?

해결책

이것은 작동해야합니다 :

def getCCParams(cc: AnyRef) =
  (Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
    f.setAccessible(true)
    a + (f.getName -> f.get(cc))
  }

다른 팁

케이스 클래스가 확장되기 때문입니다 제품 하나는 단순히 사용할 수 있습니다 .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
}

제품의 장점 중 하나는 전화 할 필요가 없다는 것입니다. setAccessible 그 가치를 읽기 위해 현장에서. 다른 하나는 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 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)

모양이없는 것을 사용할 수 있습니다.

허락하다

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]
}

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-> 자전거, a -> true)

anymapy = map (b-> 두 번째, a-> 첫 번째)

StringMapx = map (c-> 26, b-> 자전거, a -> true)

StringMapy = map (b-> 두 번째, a-> 첫 번째)

중첩 케이스 클래스 (따라서 중첩지도) 확인 또 다른 대답

솔루션 ProductCompletion 통역사 패키지 :

import tools.nsc.interpreter.ProductCompletion

def getCCParams(cc: Product) = {
  val pc = new ProductCompletion(cc)
  pc.caseNames.zip(pc.caseFields).toMap
}

JSON4를 사용하는 경우 다음을 수행 할 수 있습니다.

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]]

시작 Scala 2.13, case classES (구현으로 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")

나는 Nice에 대해 모른다 ... 그러나 이것은 적어도이 매우 기본적인 예에서는 효과가있는 것 같습니다. 일이 필요하지만 시작하기에 충분할 수 있습니까? 기본적으로 사례 클래스 (또는 다른 클래스 :/)에서 모든 "알려진"방법을 필터링합니다.

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)

세부: https://github.com/hank-whu/common4s

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top