如何在反序列化期间初始化瞬态字段?

时间:2015-02-17 19:44:23

标签: scala serialization jackson deserialization transient

以下代码显示了使用Jackson的简单类的序列化和反序列化。麻烦是在反序列化期间,Root的正常构造函数未被调用,因此name类的瞬态字段Leaf没有最初构造时的值。有没有办法如何提供具有所需值的瞬态字段,而不必使它们成为变量?一些自定义序列化程序或一些聪明的注释?

我不想序列化name值以使序列化格式尽可能紧凑 - 在数据结构给出所有值之后,应该可以再次从结构中重新创建它。

import com.fasterxml.jackson.annotation._
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

class Leaf(val value:Int, @transient val name:String) {

  def this(@JsonProperty value:Int) = this(value,"")
}

class Root(val a: Leaf, val b:Leaf)

object Main extends App {
  val om = new ObjectMapper() with ScalaObjectMapper {
    registerModule(new DefaultScalaModule)
  }

  val root = new Root(new Leaf(1,"a"), new Leaf(2, "b"))

  val out = om.writeValueAsString(root)

  println(out)

  val test = om.readValue(out, classOf[Root])

}

3 个答案:

答案 0 :(得分:2)

您始终可以使用方法命名备用构造函数中的Leaf实例。如果需要,此方法甚至可以保留状态。例如,以下内容将替代调用每个叶ab

class Leaf(val value: Int, @transient val name: String) {
    def this(@JsonProperty value:Int) = { 
        this(value, Leaf.namer.next)
    }
}

object Leaf {
    private val namer = Iterator.continually(Seq("a", "b")).flatten
}

答案 1 :(得分:0)

将子对象作为拥有对象的一部分处理

即使Leaf个对象是单独的JVM对象,从序列化的角度来看,它们也可以作为Root对象的一部分进行处理,所有命名都可以由其构造函数完成。对于此构造函数,需要对params进行注释并创建getter并注释访问内部Leaf值,而Leaf值本身标记为@transient

class Leaf(val value:Int, val name:String)

class Root(
  @(JsonProperty @param)("a") aVal: Int,
  @(JsonProperty @param)("b") bVal:Int
) {
  @transient val a = new Leaf(aVal,"a")
  @transient val b = new Leaf(bVal,"b")

  @(JsonProperty @getter) def getA = a.value
  @(JsonProperty @getter) def getB = b.value

}

使用@JsonDeserialier(as)

另一种选择是使用@JsonDeserialier(as=xxx)注释,然后您需要为每个所需的瞬态值创建子类:

class LeafA(value:Int) extends Leaf(value,"a")
class LeafB(value:Int) extends Leaf(value,"b")

case class Root(
  @JsonDeserialize(as=classOf[LeafA]) a: Leaf,
  @JsonDeserialize(as=classOf[LeafB]) b: Leaf
)

答案 2 :(得分:0)

我想问一个问题:什么需要知道叶名?

1)单从你的例子,没有人。或者也许是Root,但它已经知道哪个叶子是a或b,因为它是变量的名称。在这种情况下,只需从Leaf中删除名称,获取名称所需的方法应该从Root对象中获取。例如:

data

通过在Root中实现合适的命名规则,可以将其推广到例如一系列叶子。

2)由于某种原因,Leaf可以是两种类型中的一种,它与含有Root有关但可以独立处理。

在这种情况下,使用简单的trait /(case)类层次结构:

case class Leaf(value: Int)

class Root(val a: Leaf, val b: Leaf){
  def getLeavesWithName(): ((Leaf, String), (Leaf, String)) =
    ((a, "a"), (b,"b"))

3)Leaf需要有一个独立于Root的名称,但是没有关于命名的具体规则:在某些情况下你只需使用“a”和“b”,但这可能是任何东西。

在这种情况下,只需在Leaf序列化中保留名称(无@transient)

现在,您对上述内容做出了哪些选择,如果您的问题是关于优化Root的序列化而没有叶子名称或类型,因为Root暗示它,那么就不要在Root序列化中序列化Leaf对象。

我不熟悉你正在使用的序列化API,所以我不知道如何去做。但你需要的是   - trait Leaf{ val value: Int def name: String } case class LeafA(value: Int){ def name = "a" } case class LeafB(value: Int){ def name = "b" } case class Root(a: LeafA, b: LeafB) ,用于存储a和b的值,以及   - 一个Serializer[Root](w: Root) = Serializer[(Int, Int)].write(w.a, w.b),它将那些作为一个根实例。

你理解我的意思