2016-02-18 2 views
0

Я пытаюсь реализовать общий шаблон, с помощью которого можно генерировать маршаллеры и unmarshallers для службы HTTP REST от Akka, используя Argonaut, обрабатывая запросы и ответы уровня сущности и коллекции. У меня нет никаких проблем в реализации на уровень предприятия как таковые:Аргонавт: общий метод для кодирования/декодирования массива объектов

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

Я не использую аргонавт, но использовать спрей-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. Я не тестировал это, но надеюсь, что это приведет вас в правильное направление :)

+0

Чтобы избежать неприятностей для других в будущем, общее решение не сработало для меня. Я решил перейти к беспроблемному блесну, поскольку он вполне естественно поддерживает создание набора. Я ожидаю второго подхода к работе, однако DSL для Argonaut для пользовательских кодеков довольно обременительна. –

+0

@ cory-p-oncota Я согласен. Ну, я рад, что смогу помочь вам переключиться :) – expert

1

Аргонавт имеет предопределенные кодировщики и декодеры для неизменяемых списков, наборов, потоков и векторов 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 и Аргонавт 6,2-RC2 , но, насколько я знаю, это не должно зависеть от некоторых последних изменений.

Подобный подход работает с любой линейной или неупорядоченной однородной структурой данных, которую вы хотите представлять как массив JSON. Кроме того, это предпочтительнее для создания CodecJson: последний может быть выведен автоматически из JsonEncode и JsonDecode, но не наоборот. Таким образом, ваш набор будет сериализоваться и десериализоваться как при использовании независимо, так и в другом типе данных, как показано в примере.

Смежные вопросы