如何在反序列化期间初始化瞬态字段?
How to initialize transient fields during deserialization?
以下代码显示了使用 Jackson 对简单 类 进行序列化和反序列化。问题是在反序列化期间 Root
的正常构造函数未被调用,因此 Leaf
类 的瞬态字段 name
没有最初构造时的值。有什么方法可以为瞬态字段提供所需的值,而不必将它们设为变量?一些自定义序列化程序或一些巧妙的注释?
我不想序列化 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])
}
您始终可以使用一种方法来命名备用构造函数中的 Leaf
个实例。如有必要,此方法甚至可以保留状态。例如,以下将交替调用每个叶子 a
或 b
:
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
}
将子对象作为拥有对象的一部分进行处理
即使 Leaf
对象是单独的 JVM 对象,从序列化的角度来看,它们也可以作为 Root
对象的一部分进行处理,并且所有命名都可以由其构造函数完成。对于此构造函数参数,需要注释并创建吸气剂并注释访问内部 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
)
我会有一个疑问:什么需要知道叶名?
1) 仅以你为例,没有人。或者可能是 Root,但它已经知道哪个叶子是 a 或 b,因为它是变量的名称。在这种情况下,只需从 Leaf 中删除名称,任何需要获取名称的方法都应该从 Root 对象中获取。例如:
case class Leaf(value: Int)
class Root(val a: Leaf, val b: Leaf){
def getLeavesWithName(): ((Leaf, String), (Leaf, String)) =
((a, "a"), (b,"b"))
这可以通过在 Root 中实施合适的命名规则来推广,例如,推广到叶子序列。
2) 由于某些原因,Leaf 可以是两种类型之一,这与其包含的 Root 相关,但可以独立于它进行处理。
在这种情况下,使用简单的 trait/(case)class 层次结构:
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)
3) Leaf 需要有一个独立于 Root 的名称,但没有关于命名的特定规则:在某些情况下您只需使用 "a" 和 "b",但那可以是任何东西。
在这种情况下,只需将名称保留在 Leaf 序列化中(无 @transient)
现在,无论您对上述内容做出何种选择,如果您的问题是关于优化没有叶子名称或类型的 Root 的序列化,因为 Root 暗示它,那么就不要在 Root 序列化中序列化 Leaf 对象。
我不熟悉您使用的连载API,所以我不知道该怎么做。但你需要的是
- 存储 a 和 b 值的 Serializer[Root](w: Root) = Serializer[(Int, Int)].write(w.a, w.b)
,以及
- Deserializer[Root](obj) = Deserializer[(Int, Int)].map((a,b) => new Root(a,b))
将这些作为根实例。
你明白我的意思
以下代码显示了使用 Jackson 对简单 类 进行序列化和反序列化。问题是在反序列化期间 Root
的正常构造函数未被调用,因此 Leaf
类 的瞬态字段 name
没有最初构造时的值。有什么方法可以为瞬态字段提供所需的值,而不必将它们设为变量?一些自定义序列化程序或一些巧妙的注释?
我不想序列化 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])
}
您始终可以使用一种方法来命名备用构造函数中的 Leaf
个实例。如有必要,此方法甚至可以保留状态。例如,以下将交替调用每个叶子 a
或 b
:
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
}
将子对象作为拥有对象的一部分进行处理
即使 Leaf
对象是单独的 JVM 对象,从序列化的角度来看,它们也可以作为 Root
对象的一部分进行处理,并且所有命名都可以由其构造函数完成。对于此构造函数参数,需要注释并创建吸气剂并注释访问内部 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
)
我会有一个疑问:什么需要知道叶名?
1) 仅以你为例,没有人。或者可能是 Root,但它已经知道哪个叶子是 a 或 b,因为它是变量的名称。在这种情况下,只需从 Leaf 中删除名称,任何需要获取名称的方法都应该从 Root 对象中获取。例如:
case class Leaf(value: Int)
class Root(val a: Leaf, val b: Leaf){
def getLeavesWithName(): ((Leaf, String), (Leaf, String)) =
((a, "a"), (b,"b"))
这可以通过在 Root 中实施合适的命名规则来推广,例如,推广到叶子序列。
2) 由于某些原因,Leaf 可以是两种类型之一,这与其包含的 Root 相关,但可以独立于它进行处理。
在这种情况下,使用简单的 trait/(case)class 层次结构:
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)
3) Leaf 需要有一个独立于 Root 的名称,但没有关于命名的特定规则:在某些情况下您只需使用 "a" 和 "b",但那可以是任何东西。
在这种情况下,只需将名称保留在 Leaf 序列化中(无 @transient)
现在,无论您对上述内容做出何种选择,如果您的问题是关于优化没有叶子名称或类型的 Root 的序列化,因为 Root 暗示它,那么就不要在 Root 序列化中序列化 Leaf 对象。
我不熟悉您使用的连载API,所以我不知道该怎么做。但你需要的是
- 存储 a 和 b 值的 Serializer[Root](w: Root) = Serializer[(Int, Int)].write(w.a, w.b)
,以及
- Deserializer[Root](obj) = Deserializer[(Int, Int)].map((a,b) => new Root(a,b))
将这些作为根实例。
你明白我的意思