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含めず、代わりに JSON でエコーされる a を使用しています。bodytextEncodedBody

問題は、次のように逆シリアル化しようとすると発生します。

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

次のエラーが表示されます。

com.codahale.jerkson.ParsingException: 無効な JSON。[body] が必要でしたが、[isNativeText, textEncodedBody] が見つかりました。

明らかに、動作するコンストラクターがあります...それはデフォルトのものではありません。Jerkson にこのデフォルト以外のコンストラクターを使用させるにはどうすればよいですか?

@JsonProperty編集:と注釈の両方を使用しようとしました@JsonCreatorが、ジャークソンはそれらの両方を無視しているようです。

EDIT2 :ジャークソン ケース クラス シリアライゼーション ソース コードを見ると、そのフィールドと同じ名前のケース クラス メソッド@JsonPropertyが機能する方法、つまり JSON ゲッターとして使用されるように見えます。それができれば、私の問題は解決するでしょう。Scala にあまり詳しくないので、その方法がわかりません。ケース クラスがそのフィールドの 1 つと同じ名前のユーザー定義メソッドを持つことは可能ですか?

参考までに、この結論に導く以下のコードを次に示します...

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

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

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

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

于 2012-08-24T10:25:29.503 に答える