在scala中,如何将对象的值转换为Map [String,String]?

时间:2012-10-09 09:51:17

标签: scala

假设我有这个课程

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

和这个类的一个实例:

Test :
id -> 1
name -> toto

我想创建一个Map [String,String],如下所示:

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

我的问题是:有没有办法将此Test实例转换为Map [String,String]?我想避免使用这样的方法:

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
}

如果在Scala中没有方法可以这样做,有没有办法迭代类属性?也许我可以创建一个通用函数:

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
}

这可能吗?如果你有很好的例子/链接,我很感兴趣。

3 个答案:

答案 0 :(得分:20)

是的,这是可能的。从Scala 2.10开始,您可以使用反射。

假设你有:

val test = Test(23423, "sdlkfjlsdk")

以下内容可为您提供所需内容:

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

简单地迭代案例类的字段值,在其实例上使用.productIterator

答案 1 :(得分:10)

您正在处理的主题在StackOverFlow上变得非常复杂,如果您想要一个类型安全的实现,问题就不容小觑了。

解决问题的一种方法是使用反射(如建议的那样),但我个人更喜欢使用类型系统和暗示。

有一个着名的库,由一个非常聪明的人开发,允许你执行高级操作,例如将任何案例类转换为类型安全的异构列表,或者创建可用于实现“可扩展记录”的异常映射”。该库名为Shapeless,这里有一个例子:

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

Book的行为类似于可以使用对象作为键索引的类型安全映射。如果您有兴趣了解更多信息,可能会有一个很好的切入点:

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

答案 2 :(得分:0)

Product的开始为Scala 2.13case classes(作为productElementNames的实现)提供了productIterator方法,该方法返回其字段名称上的迭代器。

通过将字段名称与通过http://www.opa.kg/uploads/posts/2009-08/1251278848_podborochka-kartinok_37355_s__22.jpg获得的字段值进行压缩,我们可以通用地获取关联的Map[String, Any],并通过将值与toString关联的Map[String, String]进行映射:

// case class Test(id: Long, name: String)
// val x = Test(1, "todo")
(x.productElementNames zip x.productIterator.map(_.toString)).toMap
// Map[String,String] = Map("id" -> "1", "name" -> "todo")