我需要序列化/反序列化一个Scala类,其结构如下所示:
@JsonIgnoreProperties(ignoreUnknown = true, value = Array("body"))
case class Example(body: Array[Byte]) {
lazy val isNativeText = bodyIsNativeText
lazy val textEncodedBody = (if (isNativeText) new String(body, "UTF-8") else Base64.encode(body))
def this(isNativeText: Boolean, textEncodedBody: String) = this((if(isNativeText) str.getBytes("UTF-8") else Base64.decode(textEncodedBody)))
def bodyIsNativeText: Boolean = // determine if the body was natively a string or not
}它的主要成员是一个字节数组,它可能表示一个UTF-8编码的文本字符串,但可能不是。主构造函数接受一个字节数组,但是有一个备用构造函数,它接受一个带有标志的字符串,指示该字符串是base64编码的二进制数据,还是我们要存储的实际本机文本。
为了序列化到JSON对象,如果主体是本机文本,我希望将其存储为本机字符串,而不是base64 64编码的字符串。这就是为什么我使用@JsonIgnoreProperties不包含body属性,而是有一个在JSON中得到回响的textEncodedBody。
当我试图像这样反序列化它时,问题就出现了:
val e = Json.parse[Example]("""{'isNativeText': true, 'textEncodedBody': 'hello'}""")
我收到以下错误:
com.codahale.jerkson.ParsingException:无效JSON。需要身体,但找到了isNativeText,textEncodedBody。
显然,我有一个将work...it不是默认构造函数的构造函数。我如何强迫Jerkson使用这个非默认的构造函数?
编辑:我尝试同时使用@JsonProperty和@JsonCreator注解,但是jerkson似乎忽略了这两种注释。
EDIT2:纵观jerkson案例类序列化源代码,它看起来就像一个case类方法,其名称与其字段的名称相同,它将以@JsonProperty的方式使用--即作为JSON。如果我能这样做,它就能解决我的问题。由于对Scala不太熟悉,我不知道如何做到这一点;一个案例类是否有可能拥有一个与其字段名称相同的用户定义方法?
作为参考,下面的代码让我得出这样的结论.
private val methods = klass.getDeclaredMethods
.filter { _.getParameterTypes.isEmpty }
.map { m => m.getName -> m }.toMap
def serialize(value: A, json: JsonGenerator, provider: SerializerProvider) {
json.writeStartObject()
for (field <- nonIgnoredFields) {
val methodOpt = methods.get(field.getName)
val fieldValue: Object = methodOpt.map { _.invoke(value) }.getOrElse(field.get(value))
if (fieldValue != None) {
val fieldName = methodOpt.map { _.getName }.getOrElse(field.getName)
provider.defaultSerializeField(if (isSnakeCase) snakeCase(fieldName) else fieldName, fieldValue, json)
}
}
json.writeEndObject()
}发布于 2012-08-24 10:25:29
如果我错了,请纠正我,但是看起来Jackson/Jerkson不支持任意嵌套的JSON。有一个使用嵌套的wiki上的例子,但它看起来目标类必须有对应于嵌套JSON的嵌套类。
无论如何,如果您没有在case类中使用嵌套,那么只需声明第二个case类和几个隐式转换就可以了:
case class Example(body: Array[Byte]) {
// Note that you can just inline the body of bodyIsNativeText here
lazy val isNativeText: Boolean = // determine if the body was natively a string or not
}
case class ExampleRaw(isNativeText: Boolean, textEncodedBody: String)
implicit def exampleToExampleRaw(ex: Example) = ExampleRaw(
ex.isNativeText,
if (ex.isNativeText) new String(ex.body, "UTF-8")
else Base64.encode(ex.body)
)
implicit def exampleRawToExample(raw: ExampleRaw) = Example(
if (raw.isNativeText) raw.textEncodedBody.getBytes("UTF-8")
else Base64.decode(textEncodedBody)
)现在您应该能够这样做了:
val e: Example = Json.parse[ExampleRaw](
"""{'isNativeText': true, 'textEncodedBody': 'hello'}"""
)您可以保留添加的原始方法和注释,以使JSON生成继续使用Example类型,或者只需使用强制转换:
generate(Example(data): ExampleRaw)更新:
为了帮助捕获错误,您可能也需要这样做:
case class Example(body: Array[Byte]) {
// Note that you can just inline the body of bodyIsNativeText here
lazy val isNativeText: Boolean = // determine if the body was natively a string or not
lazy val doNotSerialize: String = throw new Exception("Need to convert Example to ExampleRaw before serializing!")
}如果您不小心将Example的实例而不是ExampleRaw传递给generate调用,则会引发异常。
https://stackoverflow.com/questions/12102550
复制相似问题