首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用json4s一般地将Java序列化为json

使用json4s一般地将Java序列化为json
EN

Stack Overflow用户
提问于 2018-01-11 17:17:40
回答 1查看 814关注 0票数 1

我们的finatra应用程序使用json4s在控制器响应中将对象序列化为jsons。但是,我注意到,当尝试序列化枚举时,它会创建一个空对象。

我看到了这样的响应,它可以解决我的问题,但必须为每个枚举复制:https://stackoverflow.com/a/35850126/2668545

代码语言:javascript
复制
class EnumSerializer[E <: Enum[E]](implicit ct: Manifest[E]) extends CustomSerializer[E](format ⇒ ({
  case JString(name) ⇒ Enum.valueOf(ct.runtimeClass.asInstanceOf[Class[E]], name)
}, {
  case dt: E ⇒ JString(dt.name())
}))

// first enum I could find
case class X(a: String, enum: java.time.format.FormatStyle)
implicit val formats = DefaultFormats + new EnumSerializer[java.time.format.FormatStyle]()

// {"a":"test","enum":"FULL"}
val jsonString = Serialization.write(X("test", FormatStyle.FULL))
Serialization.read[X](jsonString)

是否有一种方法可以通过在序列化到json时获取它们的.name()值来处理所有java枚举实例的通用自定义序列化程序?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-12 20:33:37

我不认为有一个清洁的解决方案,因为类型安全的限制。不过,如果您对依赖Java使用类型擦除这一事实的hacky解决方案感到满意,那么这里有一个似乎有效的解决方案:

代码语言:javascript
复制
class EnumSerializer() extends Serializer[Enum[_]] {
  override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Enum[_]] = {
    // using Json4sFakeEnum is a huge HACK here but it seems to  work
    case (TypeInfo(clazz, _), JString(name)) if classOf[Enum[_]].isAssignableFrom(clazz) => Enum.valueOf[Json4sFakeEnum](clazz.asInstanceOf[Class[Json4sFakeEnum]], name)
  }

  override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
    case v: Enum[_] => JString(v.name())
  }
}

其中Json4sFakeEnum实际上是在Java中定义的假enum (实际上,任何enum都应该工作,但我更喜欢让它显式地伪造)

代码语言:javascript
复制
enum Json4sFakeEnum {
}

有了这样的定义,一个类似于你的例子

代码语言:javascript
复制
// first enum I could find
case class X(a: String, enum: java.time.format.FormatStyle)

def js(): Unit = {
  implicit val formats = DefaultFormats + new EnumSerializer()

  val jsonString = Serialization.write(X("test", FormatStyle.FULL))
  println(s"jsonString '$jsonString'")
  val r = Serialization.read[X](jsonString)
  println(s"res ${r.getClass} '$r'")
}

产生下列产出:

jsonString '{"a":"test","enum":"FULL"}‘res class so.Main$X 'X(test,FULL)’

Json4sFakeEnum**?**更新或它是如何工作的,以及为什么需要

有两件重要的事情:

  1. 扩展Serializer而不是CustomSerializer。这一点很重要,因为它允许创建一个可以处理所有Enum类型的单个非泛型实例。这是因为Serializer.deserialize创建的函数接收TypeInfo作为参数,因此它可以分析运行时类。
  2. Json4sFakeEnum黑客。从高层的角度来看,只要有一个给定枚举的Class就可以得到所有的名称,因为它们存储在Class对象中。但是,在实现详细信息级别上,最简单的访问方式是使用具有以下签名的Enum.valueOf方法:
代码语言:javascript
复制
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)

不幸的是,它有一个通用签名,并且有一个限制T extends Enum<T>。这意味着即使我们有适当的Class对象,我们所知道的最好的类型仍然是Enum[_],这不符合extends Enum<T>的自引用限制。另一方面,Java使用类型擦除,因此valueOf实际上被编译成如下所示

代码语言:javascript
复制
public static Enum<?> valueOf(Class<Enum<?>> enumType, String name)

这意味着,如果我们只是欺骗编译器,让我们调用valueOf,在运行时一切都会好起来。这就是Json4sFakeEnum出现的地方:我们只需要在编译时知道一些特定于Enum的子类来进行valueOf调用。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48212609

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档