使用Jerkson的非默认构造函数?

时间:2012-08-24 02:42:25

标签: json scala jackson

我需要使用如下结构序列化/反序列化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()
  }

1 个答案:

答案 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来电,那么这会导致异常抛出。