使用Jerkson的非默认构造函数? [英] Use the non-default constructor with Jerkson?

查看:144
本文介绍了使用Jerkson的非默认构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要使用如下结构序列化/反序列化Scala类:

I need to serialize/deserialize a Scala class with structure something like the following:

@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编码的二进制数据,或者我们想要存储的实际本机文本。

It's main member is an array of bytes, which MIGHT represent a UTF-8 encoded textual string, but might not. The primary constructor accepts an array of bytes, but there is an alternate constructor which accepts a string with a flag indicating whether this string is base64 encoded binary data, or the actual native text we want to store.

为了序列化为JSON对象,我希望将主体存储为本机字符串,而不是base64编码的字符串(如果它是本机文本)。这就是为什么我使用 @JsonIgnoreProperties 来不包含 body 属性,而是使用 textEncodedBody 在JSON中得到回应。

For serializing to a JSON object, I want to store the body as a native string rather than a base64-encoded string if it is native text. That's why I use @JsonIgnoreProperties to not include the body property, and instead have a textEncodedBody that gets echoed out in the JSON.

当我尝试反序列化时出现问题:

The problem comes when I try to deserialize it like so:

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

我收到以下错误:


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

com.codahale.jerkson.ParsingException: Invalid JSON. Needed [body], but found [isNativeText, textEncodedBody].

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

Clearly, I have a constructor that will work...it just is not the default one. How can I force Jerkson to use this non-default constructor?

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

I've attempted to use both the @JsonProperty and @JsonCreator annotation, but jerkson appears to disregard both of those.

EDIT2:看着 jerkson案例类序列化源代码,它看起来像一个与其字段同名的案例类方法将以 @JsonProperty 的函数方式使用 - 也就是说,作为JSON getter。如果我能做到这一点,它将解决我的问题。不熟悉Scala,我不知道该怎么做;案例类是否可以使用与其中一个字段具有相同名称的用户定义方法?

Looking over the jerkson case class serialization source code, it looks like a case class method with the same name as its field will be used in the way that a @JsonProperty would function - that is, as a JSON getter. If I could do that, it would solve my problem. Not being super familiar with Scala, I have no idea how to do that; is it possible for a case class to have a user-defined method with the same name as one of its fields?

作为参考,以下代码引导我得出这个结论...

For reference, here is the code below that leads me to this conclusion...

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()
  }


推荐答案

如果我错了,请纠正我,但杰克逊/杰克逊看起来不会支持任意嵌套的JSON。有一个使用嵌套的 wiki上的示例,但它看起来像目标类必须具有与嵌套JSON相对应的嵌套类。

Correct me if I'm wrong, but it looks like Jackson/Jerkson will not support arbitrarily nested JSON. There's an example on the wiki that uses nesting, but it looks like the target class must have nested classes corresponding to the nested JSON.

无论如何,如果你没有在案例类中使用嵌套,那么只需声明第二个案例类和几个隐式转换应该工作正常:

Anyway, if you're not using nesting with your case classes then simply declaring a second case class and a couple implicit conversions should work just fine:

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)
)

现在你应该可以这样做:

Now you should be able to do this:

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

您可以保留您添加的原始方法和注释,以使JSON生成继续与示例类型,或者您可以使用强制转换进行转换:

You could leave the original methods and annotations you added to make the JSON generation continue to work with the Example type, or you could just convert it with a cast:

generate(Example(data): ExampleRaw)

更新:

Update:

为了帮助您发现错误,您可能也想做同样的事情:

To help catch errors you might want to do something like this too:

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!")
}

如果您不小心传递示例的实例,那么应该引发异常 ExampleRaw 生成电话。

That should cause an exception to be thrown if you accidentally pass an instance of Example instead of ExampleRaw to a generate call.

这篇关于使用Jerkson的非默认构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆