我正在使用json4s 3.2.11和Scala 2.11。
我有一个使用sealed trait
定义的枚举,以及一个针对它的自定义序列化器:
import org.json4s.CustomSerializer
import org.json4s.JsonAST.JString
import org.json4s.DefaultFormats
import org.json4s.jackson.Serialization
sealed trait Foo
case object X extends Foo
case object Y extends Foo
object FooSerializer
extends CustomSerializer[Foo](
_ =>
({
case JString("x") => X
case JString("y") => Y
}, {
case X => JString("x")
case Y => JString("y")
})
)
这很好,添加到以下格式后效果很好:
{
implicit val formats = DefaultFormats + FooSerializer
Serialization.write(X) // "x"
}
太好了!
如果未将序列化程序添加到格式中,则json4s将使用反射来创建字段的默认表示形式,这对于这些没有字段的object
极为不利。它无声地执行此操作,似乎无法控制它。
{
implicit val formats = DefaultFormats
Serialization.write(X) // {}
}
这是一个问题,因为直到很久以后才有迹象表明出了什么问题。如果没有碰巧捕获到这些无效/无用的数据,它们可能会通过网络发送或写入数据库。而且,这可能是从图书馆公开公开的,这意味着下游用户也必须记住它。
NB。这与read
不同,后者在失败时引发异常,因为Foo
特性没有任何有用的构造函数:
{
implicit val formats = DefaultFormats
Serialization.read[Foo]("\"x\"")
}
org.json4s.package$MappingException: No constructor for type Foo, JString(x)
at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$constructor(Extraction.scala:417)
at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$instantiate(Extraction.scala:468)
at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$result$6.apply(Extraction.scala:515)
...
是否有办法为这些对象禁用默认的{}
格式,或者将格式“烘焙”到对象本身?
例如,让write
抛出类似read
的异常会很好,因为它会立即将问题标记给调用者。
答案 0 :(得分:3)
有一个古老的open issue似乎在问类似的问题,其中contributors之一建议
您需要创建自定义解串器或序列化器
这听起来好像没有现成的方法可以更改默认行为。
尝试禁止使用Scalastyle IllegalImportsChecker
导入UNWIND $filenames AS filename
LOAD CSV WITH HEADERS FROM "file:///"+filename+".csv" AS data
CREATE (f:File {filename:filename})-[:FOLLOWS]->(r:Row)
SET r.csv_row = data
org.json4s.DefaultFormats
并像这样提供自定义 <check level="error" class="org.scalastyle.scalariform.IllegalImportsChecker" enabled="true">
<parameters>
<customMessage>Import from illegal package: Please use example.DefaultFormats instead of org.json4s.DefaultFormats</customMessage>
<parameter name="illegalImports"><![CDATA[org.json4s.DefaultFormats]]></parameter>
</parameters>
</check>
DefaultFormats
这将允许我们像这样序列化ADT
package object example {
val DefaultFormats = Serialization.formats(NoTypeHints) + FooSerializer
}
应输出
import example.DefaultFormats
implicit val formats = DefaultFormats
case class Bar(foo: Foo)
println(Serialization.write(Bar(X)))
println(Serialization.write(X))
println(Serialization.write(Y))
如果我们尝试导入{"foo":"x"}
"x"
"y"
,则Scalastyle应该会引发以下错误:
org.json4s.DefaultFormats
也许我们可以通过在Import from illegal package: Please use example.DefaultFormats instead of org.json4s.DefaultFormats
中定义write
方法来将格式“烘焙”到对象中,就像这样委托给Foo
Serialization.write
请注意我们如何将sealed trait Foo {
object FooSerializer extends CustomSerializer[Foo](_ =>
({
case JString("x") => X
case JString("y") => Y
}, {
case X => JString("x")
case Y => JString("y")
})
)
def write: String =
Serialization.write(this)(DefaultFormats + FooSerializer)
}
case object X extends Foo
case object Y extends Foo
格式硬编码为FooSerializer
。现在我们可以序列化了
write
应输出
println(X.write)
println(Y.write)
"x"
"y"
旁边提供自定义DefaultFormats
我们也可以尝试像这样在我们自己的包中定义自定义org.json4s.DefaultFormats
DefaultFormats
这将允许我们像这样序列化ADT
package example
object DefaultFormats extends DefaultFormats {
override val customSerializers: List[Serializer[_]] = List(FooSerializer)
}
应输出
import example.DefaultFormats
implicit val formats = DefaultFormats
case class Bar(foo: Foo)
println(Serialization.write(Bar(X)))
println(Serialization.write(X))
println(Serialization.write(Y))
具有{"foo":"x"}
"x"
"y"
和org.json4s.DefaultFormats
两种默认格式,至少会使用户不得不在两者之间进行选择,如果说,他们使用IDE来自动导入它们。