我正在尝试使用Argonaut实现一个通用模式,用于为Akka HTTP REST服务生成marshallers和unmarshallers,处理实体和集合级别的请求和响应。我在实现实体级别方面没有任何问题:
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
我看到这种方法确实不存在,但是有没有其他通用方法来生成我期望的结果。我强烈避免使用额外的case类来描述集合,因为我在用例中大量使用了反射。
此时,我甚至可以使用手动构建的编码器和解码器,但是,我没有找到关于如何使用预期结构构建它的文档。
答案 0 :(得分:1)
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自动推断,但反之亦然。这样,当您单独使用或在其他数据类型中使用时,您的集将序列化和反序列化,如示例所示。
答案 1 :(得分:0)
我不使用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。我没有对此进行测试,但希望它能引导您找到正确的方向:)