2012-10-09 25 views
11

Digamos que tengo esta claseEn scala, ¿cómo convertir los valores del objeto en Map [String, String]?

case class Test (id: Long, name: String) 

y una instancia de esta clase:

Test : 
id -> 1 
name -> toto 

me gustaría crear un mapa [String, String] de la siguiente manera:

Map("id" -> "1", "name" -> "toto") 

Mi pregunta es: ¿hay alguna manera de convertir esta instancia de Prueba en Mapa [Cadena, Cadena]? Quiero evitar utilizar un método como éste:

def createMap(instance: Test): Map[String, String] = { 
    val map = new Map[String, String] 
    map.put("id", instance.id.toString) 
    map.put("name", instance.name) 
    map 
} 

Si no existe un método para hacerlo en Scala, hay una manera de iterar sobre las propiedades de clase? Tal vez puedo crear una función genérica para hacerlo:

def createMap(instance: T): Map[String, String] = { 
    val map = new Map[String, String] 
    //pseudocode 
    for ((name, value) <- instance.getClassProperties.getValues) { 
     case value.isInstanceOf[String] : map.push(name, value) 
     case _ : map.push(name, value.toString) 
    } 
    map 
} 

¿Es esto posible? Si tiene buenos ejemplos/enlaces, estoy interesado.

+0

Las respuestas a [esta pregunta] (http://stackoverflow.com/questions/2224251/reflection-on-a-scala-case-class) pueden ayudar. – incrop

+0

No es que no me gusten las soluciones genéricas, pero para este problema usaría 'Map (" id "-> t.id.toString," name "-> t.name)' donde 't' es una instancia de 'Prueba'. – sschaef

+0

¡Gracias por el consejo! – alexgindre

Respuesta

19

Sí, es posible. Desde Scala 2.10 puedes usar la reflexión.

Suponiendo que tienen:

val test = Test(23423, "sdlkfjlsdk") 

A continuación le conseguirá lo que quiere:

import reflect.runtime.universe._ 
import reflect.runtime.currentMirror 

val r = currentMirror.reflect(test) 
r.symbol.typeSignature.members.toStream 
    .collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)} 
    .map(r => r.symbol.name.toString.trim -> r.get.toString) 
    .toMap 

simplemente iterar sobre los valores de campo de uso de clase .productIterator caso en su instancia.

+0

¿Se puede revertir este proceso? De mapa a objeto? Si se da el nombre completo de la clase –

+1

@JeffLee Sí. Es posible tanto con reflexión como con macros. Aquí está [un ejemplo usando el reflejo de Scala 2.10] (https://github.com/sorm/sorm/blob/master/src/main/scala/sorm/reflection/Reflection.scala#L50-L58). –

10

El tema que está tratando se está volviendo increíblemente recurrente en StackOverFlow y el problema no es trivial si desea tener una implementación de tipo seguro.

Una forma de resolver el problema es utilizando la reflexión (como se sugiere) pero yo personalmente prefiero usar el sistema de tipos y las implicaciones.

Hay una biblioteca bien conocida, desarrollada por un tipo extremadamente inteligente, que le permite realizar operaciones avanzadas como convertir cualquier clase de caso en una lista heterogénea tipo seguro, o crear mapas heterogéneos, que se pueden utilizar para implementar "extensible" archivos". La biblioteca se llama Sin forma, y ​​aquí un ejemplo:

object RecordExamples extends App { 
    import shapeless._ 
    import HList._ 
    import Record._ 

    object author extends Field[String] { override def toString = "Author" } 
    object title extends Field[String] { override def toString = "Title" } 
    object id  extends Field[Int]  { override def toString = "ID" } 
    object price extends Field[Double] { override def toString = "Price" } 
    object inPrint extends Field[Boolean] { override def toString = "In print" } 

    def printBook[B <: HList](b : B)(implicit tl : ToList[B, (Field[_], Any)]) = { 
    b.toList foreach { case (field, value) => println(field+": "+value) } 
    println 
    } 

    val book = 
    (author -> "Benjamin Pierce") :: 
    (title -> "Types and Programming Languages") :: 
    (id  -> 262162091) :: 
    (price -> 44.11) :: 
    HNil 

    printBook(book) 

    // Read price field 
    val currentPrice = book.get(price) // Static type is Double 
    println("Current price is "+currentPrice) 
    println 

    // Update price field, relying on static type of currentPrice 
    val updated = book + (price -> (currentPrice+2.0)) 
    printBook(updated) 

    // Add a new field 
    val extended = updated + (inPrint -> true) 
    printBook(extended) 

    // Remove a field 
    val noId = extended - id 
    printBook(noId) 
} 

libro se comporta como un mapa que puede typesafe indexados usando objetos como claves. Si usted está interesado en saber más, un buen punto de entrada puede aparecer este mensaje:

Are HLists nothing more than a convoluted way of writing tuples?

Cuestiones relacionadas