以下代码显示了使用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])
}
答案 0 :(得分:2)
您始终可以使用方法命名备用构造函数中的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
}
答案 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=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)
,它将那些作为一个根实例。
你理解我的意思