簡體   English   中英

Argonaut:編碼/解碼對象數組的通用方法

[英]Argonaut: Generic method to encode/decode array of objects

我正在嘗試實現一種通用模式,使用該模式可以使用Argonaut為Akka HTTP REST服務生成編組器和解組器,同時處理實體和集合級別的請求和響應。 在實現這樣的實體級別上,我沒有任何問題:

case class Foo(foo: String)

object Foo {
  implicit val FooJsonCodec = CodecJson.derive[Foo]

  implicit val EntityEncodeJson = FooJson.Encoder

  implicit val EntityDecodeJson = FooJson.Decoder
}

我在嘗試為以下內容提供編碼器和解碼器時遇到問題:

[
  { "foo": "1" },
  { "foo": "2" }
]

我試圖將以下內容添加到我的同伴中:

object Foo {
  implicit val FooCollectionJsonCodec = CodecJson.derive[HashSet[Foo]]
}

但是,我收到以下錯誤:

Error:(33, 90) value jencode0L is not a member of object argonaut.EncodeJson

我看到這種方法確實不存在,但是還有其他通用方法可以產生預期的結果。 我極力避免使用其他案例類來描述集合,因為我在用例中大量使用了反射。

在這一點上,我什至可以使用手動構造的Encoder和Decoder很好,但是,我還沒有找到有關如何使用預期結構構造它的文檔。

Argonaut具有針對Scala的不可變列表,集合,流和向量的預定義編碼器和解碼器。 如果不明確支持您的類型(如java.util.HashSet的情況),則可以為該類型輕松添加EncodeJson和DecodeJson:

import argonaut._, Argonaut._
import scala.collection.JavaConverters._

implicit def hashSetEncode[A](
  implicit element: EncodeJson[A]
): EncodeJson[java.util.HashSet[A]] =
  EncodeJson(set => EncodeJson.SetEncodeJson[A].apply(set.asScala.toSet))

implicit def hashSetDecode[A](
  implicit element: DecodeJson[A]
): DecodeJson[java.util.HashSet[A]] =
  DecodeJson(cursor => DecodeJson.SetDecodeJson[A]
    .apply(cursor)
    .map(set => new java.util.HashSet(set.asJava)))

// Usage:

val set = new java.util.HashSet[Int]
set.add(1)
set.add(3)
val jsonSet = set.asJson // [1, 3]
jsonSet.jdecode[java.util.HashSet[Int]] // DecodeResult(Right([1, 3]))

case class A(set: java.util.HashSet[Int])
implicit val codec = CodecJson.derive[A]
val a = A(set)
val jsonA = a.asJson // { "set": [1, 3] }
jsonA.jdecode[A] // DecodeResult(Right(A([1, 3])))

在Scala 2.12.1和Argonaut 6.2-RC2上檢查了示例,但據我所知它不應該依賴於某些最新更改。

這樣的方法適用於您要表示為JSON數組的任何線性或無序同構數據結構。 同樣,這比創建CodecJson更可取:可以從JsonEncode和JsonDecode自動推斷出后者,反之則不然。 這樣,您的集合將在獨立使用或在其他數據類型內使用時進行序列化和反序列化,如示例所示。

我不使用Argonaut,但使用spray-json,懷疑解決方案可能相似。

你嘗試過這樣的事情嗎?

implicit def HashSetJsonCodec[T : CodecJson] = CodecJson.derive[Set[T]]

如果不起作用,我可能會嘗試創建更多詳細的隱式函數,例如

implicit def SetJsonCodec[T: CodecJson](implicit codec: CodecJson[T]): CodecJson[Set[T]] = {
  CodecJson(
    {
      case value => JArray(value.map(codec.encode).toList)
    },
    c => c.as[JsonArray].flatMap {
      case arr: Json.JsonArray =>
        val items = arr.map(codec.Decoder.decodeJson)
        items.find(_.isError) match {
          case Some(error) => DecodeResult.fail[Set[T]](error.toString(), c.history)
          case None => DecodeResult.ok[Set[T]](items.flatMap(_.value).toSet[T])
        }
    }
  )
}

PS。 我沒有對此進行測試,但希望它能將您引向正確的方向:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM