0

我需要序列化/反序列化具有如下结构的 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 编码的字符串(如果它是本机文本)。这就是为什么我习惯@JsonIgnoreProperties不包含该body属性,而是textEncodedBody在 JSON 中回显一个。

当我尝试像这样反序列化它时,问题就来了:

val e = Json.parse[Example]("""{'isNativeText': true, 'textEncodedBody': 'hello'}""")

我收到以下错误:

com.codahale.jerkson.ParsingException:无效的 JSON。需要 [body],但找到了 [isNativeText, textEncodedBody]。

显然,我有一个可以工作的构造函数……它只是不是默认的。如何强制 Jerkson 使用这个非默认构造函数?

编辑:我尝试同时使用@JsonPropertyand@JsonCreator注释,但 jerkson 似乎忽略了这两个。

EDIT2:查看jerkson 案例类序列化源代码,它看起来像一个与其字段同名的案例类方法将以 a@JsonProperty函数的方式使用 - 即作为 JSON getter。如果我能做到这一点,它将解决我的问题。对 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()
  }
4

1 回答 1

1

如果我错了,请纠正我,但看起来 Jackson/Jerkson 将不支持任意嵌套的 JSON。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如果您不小心将实例而不是传递ExampleRawgenerate调用,这应该会导致引发异常。

于 2012-08-24T10:25:29.503 回答