복붙노트

[SCALA] 케이스 클래스는 스칼라에서지도로

SCALA

케이스 클래스는 스칼라에서지도로

나는 스칼라의 경우 클래스 인스턴스를 변환 할 수있는 좋은 방법은, 예를 들어, 거기에

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"

그래서 나는 비슷한 솔루션을하지만 케이스 클래스 필드를 찾고 있어요. 나는 해결책은 자바 반사를 사용해야 할 수도 있습니다 상상 싶지만, 만약 스칼라의 릴리스에서 깰 수있는 무언가를 쓰기 싫어 경우 클래스 변경의 기본이되는 구현이.

현재 내가 스칼라 서버 작업과 프로토콜을 정의하고이에 대한 이러한 아름답고 간결한 구조이기 때문에 모든 메시지와 예외 사례 클래스를 사용하고 있습니다. 하지만 다음 모든 클라이언트 구현이 사용하는 메시징 층 상에 전송하는 자바지도로 번역해야합니다. 내 현재의 구현은 개별적으로 각각의 경우 클래스에 대한 번역을 정의하지만 일반화 된 해결책을 찾기 위해 좋을 것이다.

해결법

  1. ==============================

    1.이 작업을해야합니다 :

    이 작업을해야합니다 :

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

    2.경우 클래스 제품을 확장하기 때문에 하나는 단순히 필드 값을 얻을 수 .productIterator을 사용할 수 있습니다 :

    경우 클래스 제품을 확장하기 때문에 하나는 단순히 필드 값을 얻을 수 .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이 반사를 사용하지 않는다는 것입니다.

    이 예는 다른 클래스를 확장하지 않고 생성자 외부 필드를 선언하지 않는 간단한 경우 클래스와 함께 작동합니다.

  3. ==============================

    3.사람이 재귀 버전을 보인다면, 여기 안드레이의 솔루션 @의 수정은 다음과 같습니다

    사람이 재귀 버전을 보인다면, 여기 안드레이의 솔루션 @의 수정은 다음과 같습니다

    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
    }
    

    또한 중첩의 모든 수준에서지도에 중첩 된 경우 - 클래스를 확장합니다.

  4. ==============================

    4.여기에 당신이 그것을 일반적인 기능을 신경 쓰지 않는 경우 간단한 변화는 다음과 같습니다

    여기에 당신이 그것을 일반적인 기능을 신경 쓰지 않는 경우 간단한 변화는 다음과 같습니다

    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)
    
  5. ==============================

    5.당신은 볼품 사용할 수 있습니다.

    당신은 볼품 사용할 수 있습니다.

    허락하다

    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 방법을 제공하기 위해 두 typeclasses 정의

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

    이는 인쇄

    중첩 된 경우 클래스의 경우, (따라서 중첩 된지도) 다른 답변을 확인

  6. ==============================

    6.스칼라 2.13부터 (제품의 구현 등)의 경우 클래스가 자신의 필드의 이름 반복자를 반환하는 productElementNames 방법으로 제공됩니다.

    스칼라 2.13부터 (제품의 구현 등)의 경우 클래스가 자신의 필드의 이름 반복자를 반환하는 productElementNames 방법으로 제공됩니다.

    productIterator 얻은 필드 값 필드 이름을 압축하는함으로써 우리는 일반적으로 관련지도를 얻을 수 있습니다 :

    // 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")
    
  7. ==============================

    7.통역 패키지에서 ProductCompletion과 해결 방법 :

    통역 패키지에서 ProductCompletion과 해결 방법 :

    import tools.nsc.interpreter.ProductCompletion
    
    def getCCParams(cc: Product) = {
      val pc = new ProductCompletion(cc)
      pc.caseNames.zip(pc.caseFields).toMap
    }
    
  8. ==============================

    8.당신이 Json4s를 사용하는 일 경우, 다음을 수행 할 수 있습니다 :

    당신이 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]]
    
  9. ==============================

    9.나는 좋은 모르는 ... 그러나 이것은 적어도이 아주 아주 기본적인 예를 들어, 작동하는 것 같다. 아마 몇 가지 작업을 필요로하지만, 당신이 시작하는만큼 될 수 있을까요? (: / 또는 다른 클래스) 기본적으로이 경우 클래스의 모든 "알려진"방법을 필터링

    나는 좋은 모르는 ... 그러나 이것은 적어도이 아주 아주 기본적인 예를 들어, 작동하는 것 같다. 아마 몇 가지 작업을 필요로하지만, 당신이 시작하는만큼 될 수 있을까요? (: / 또는 다른 클래스) 기본적으로이 경우 클래스의 모든 "알려진"방법을 필터링

    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)
      }
    }
    
  10. ==============================

    10.

    commons.mapper.Mappers.Mappers.beanToMap(caseClassBean)
    

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

  11. from https://stackoverflow.com/questions/1226555/case-class-to-map-in-scala by cc-by-sa and MIT license