关于反序列化:通过密封特征的scala ADT-有没有办法以通用方式从字符串反序列化

scala ADTs via sealed traits - is there a way to deserialize from string in a generic fashion

假设我具有以下特征

1
2
3
trait Named {
  def name: String
}

和以下代数数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
sealed trait Animal extends Named

case object Dog extends Animal {
  override val name: String ="dog man"
}

case object Cat extends Animal {
  override val name: String ="cat man"
}

case object Owl extends Animal {
  override val name: String ="I am an owl left in the dark"
}

现在,可以使用以下方法将字符串实例反序列化到Animal ADT中。

1
2
3
4
5
6
7
object Animal {

  def apply(name: String): Animal = name match {
    case Dog.name => Dog
    case Cat.name => Cat
  }
}

@oxbow_lakes在回答的结尾提到:

Can't instantiate easily from persisted value. This is also true but,
except in the case of huge enumerations (for example, all currencies),
this doesn't present a huge overhead.

我发现以下事实:当您添加新值时,需要将其显式添加到反序列化代码中,因为它容易出错(我认为编译器会向我警告穷举匹配,但请看一下上面的Owlapply方法-没有发出警告...)

有没有更好的办法? (如果不使用标准的scala工具集,是第三方的吗?)


此问题已由enumeratum库解决:
https://github.com/lloydmeta/enumeratum

您的代码可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import enumeratum._
import enumeratum.EnumEntry.Lowercase

sealed trait Animal extends EnumEntry with Lowercase
object Animal extends Enum[Animal] {
  val values = findValues

  case object Dog extends Animal
  case object Cat extends Animal
  case object Owl extends Animal
}

val dogName = Animal.Dog.entryName
val dog = Animal.withNameInsensitive(dogName)

您可以尝试做的一件事是使用反射获取扩展Animal的类型集,然后使用反射类型使用name查找对象值创建Map[String,Animal],然后在Animal.apply函数中使用映射。

有关获取Animal子类的更多信息,请参考此问题。