我需要使用如下结构序列化/反序列化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
}
它的主要成员是一个字节数组,MIGHT表示UTF-8编码的文本字符串,但可能不是。主构造函数接受一个字节数组,但是有一个替代构造函数接受一个带有一个标志的字符串,该标志指示该字符串是否为base64编码的二进制数据,或者我们想要存储的实际本机文本。
为了序列化为JSON对象,我希望将主体存储为本机字符串,而不是base64编码的字符串(如果它是本机文本)。这就是为什么我使用@JsonIgnoreProperties
不包含body
属性,而是让textEncodedBody
在JSON中得到回应。
当我尝试反序列化时,问题出现了:
val e = Json.parse[Example]("""{'isNativeText': true, 'textEncodedBody': 'hello'}""")
我收到以下错误:
com.codahale.jerkson.ParsingException:无效的JSON。需要[身体], 但找到了[isNativeText,textEncodedBody]。
显然,我有一个可以工作的构造函数......它不是默认构造函数。如何强制Jerkson使用这个非默认构造函数?
编辑:我试图同时使用@JsonProperty
和@JsonCreator
注释,但是jerkson似乎忽略了这两个注释。
EDIT2:查看jerkson case class serialization source code,它看起来像一个与其字段同名的案例类方法将以@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()
}
答案 0 :(得分:1)
如果我错了,请纠正我,但看起来Jackson / Jerkson不会支持任意嵌套的JSON。有一个example on the wiki使用嵌套,但看起来目标类必须具有与嵌套JSON相对应的嵌套类。
无论如何,如果您没有在案例类中使用嵌套,那么只需声明第二个案例类和一些隐式转换应该可以正常工作:
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
来电,那么这会导致异常抛出。