В scala, как преобразовать значения объектов в Map [String, String]?
Скажем, что у меня этот класс
case class Test (id: Long, name: String)
и экземпляр этого класса:
Test :
id -> 1
name -> toto
Я хотел бы создать карту [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
}
Это возможно? Если у вас есть хорошие примеры/ссылки, мне интересно.
Ответы
Ответ 1
Да, это возможно. Поскольку 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
Чтобы просто перебрать значения полей класса case, используйте .productIterator
в своем экземпляре.
Ответ 2
Тема, с которой вы имеете дело, становится невероятно повторяющейся в StackOverFlow, и проблема не является тривиальной, если вы хотите иметь типичную реализацию.
Один из способов решения проблемы - использовать рефлексию (как было предложено), но я лично предпочитаю использовать систему типов и implicits.
Существует известная библиотека, разработанная чрезвычайно умным парнем, которая позволяет вам выполнять расширенные операции, такие как включение любого класса case в разнотипный список типов или создание гетерогенных карт, которые могут использоваться для реализации "расширяемых записей" ". Библиотека называется 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)
}
Книга ведет себя как карта типов, которая может индексироваться с использованием объектов в виде ключей. Если вам интересно узнать больше, хорошей точкой входа может быть этот пост:
Являются ли HLists не более чем запутанным способом написания кортежей?