简体   繁体   English

如何使用Jackson将Scala值类序列化为字符串?

[英]How to use Jackson to serialize Scala value classes as strings?

I want to use Scala's value classes (or a normal case class) to give stronger types to some strings in my program. 我想使用Scala的值类(或普通的案例类)为程序中的某些字符串提供更强的类型。 When I serialize instances of these classes using Jackson I want them to be strings. 当我使用Jackson序列化这些类的实例时,我希望它们是字符串。

For example: 例如:

case class Brand(name: String) extends AnyVal
val brands = Seq(Brand("Coke"), Brand("Disney"))
val brandCount = Map(Brand("Coke") -> 5, Brand("Disney") -> 10)

Since Brand is just a wrapper for a String, I want the corresponding JSON serialization for these variables to be: 由于Brand仅是String的包装,因此我希望这些变量的JSON序列化为:

brands:     ["Coke", "Disney"]
brandCount: {"Coke": 5, "Disney": 10}

By default I get: 默认情况下,我得到:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule

val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)

println(mapper.writeValueAsString(brands))
// ==> [{"name":"Coke"},{"name":"Disney"}]
println(mapper.writeValueAsString(brandCount))
// ==> {"Brand(Coke)":5,"Brand(Disney)":10}

The best I could come up with is to define a custom serializer and key serializer for Brand : 我能想到的最好的办法是为Brand定义一个自定义序列化程序和密钥序列化程序:

import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.core.JsonGenerator

class BrandSerializer extends JsonSerializer[Brand] {
  override def serialize(
    b: Brand,
    json: JsonGenerator,
    provider: SerializerProvider
  ): Unit = {
    json.writeString(b.name)
  }
}

class BrandKeySerializer extends JsonSerializer[Brand] {
  override def serialize(
    b: Brand, 
    json: JsonGenerator,
    provider: SerializerProvider
  ): Unit = {
    json.writeFieldName(b.name)
  }
}

val serializers = new SimpleModule("Serializers");
serializers.addSerializer(classOf[Brand], new BrandSerializer())
serializers.addKeySerializer(classOf[Brand], new BrandKeySerializer());

val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
mapper.registerModule(serializers)

println(mapper.writeValueAsString(brands))
// ==> ["Coke","Disney"]
println(mapper.writeValueAsString(brandCount))
// ==> {"Coke":5,"Disney":10}

Is there a better (or less verbose) way to serialize these value classes (or any case class) as strings? 是否有更好(或更冗长)的方法来将这些值类(或任何case类)序列化为字符串?

Well, I don't think, there is a way to make it do all you want, and nothing you don't want in one go ... The best thing you can do that comes to mind is replace Brand with Product1 , and b.name with b._1 (I also think, your key serializer is wrong - it should be writing out b.getClass.getSimpleName , not not b.name ). 好吧,我不认为,有一种方法可以使它满足您的所有需求,而且一劳永逸。您可以想到的最好的事情就是用Product1替换Brand ,然后b.nameb._1 (我也认为,您的密钥序列化器是错误的-应该写出b.getClass.getSimpleName ,而不是b.name )。

That wouldn't make it any less verbose for writing out a single class, but will at least eliminate the need to create a separate serializer for every type like that. 这样做不会使编写单个类变得那么冗长,但是至少会消除为每种类型创建单独的序列化器的需要。

The downside, of course, is that every one-field case class will end up written this way, which may be more than what you wished for .. 不利的一面是, 每个领域案例类最终都将以这种方式编写,这可能超出您的期望。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM