Skip to content

Instantly share code, notes, and snippets.

@ferhtaydn
Last active October 2, 2017 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ferhtaydn/72cc8820a5a14f0aa8b5a9ebad016089 to your computer and use it in GitHub Desktop.
Save ferhtaydn/72cc8820a5a14f0aa8b5a9ebad016089 to your computer and use it in GitHub Desktop.
How to serialize NonEmptyList in model like normal List
import cats.Applicative
import cats.data.{NonEmptyList => NEL}
import cats.syntax.traverse._
import play.api.libs.json._
final case class Error(code: String, description: String)
object Error {
implicit object jsResultApplicative extends Applicative[JsResult] {
override def pure[A](x: A): JsResult[A] =
JsResult.applicativeJsResult.pure(x)
override def ap[A, B](
ff: JsResult[(A) => B]
)(fa: JsResult[A]): JsResult[B] =
JsResult.applicativeJsResult.apply(ff, fa)
}
implicit val errorFormat: OFormat[Error] =
Json.format[Error]
implicit val nelErrorReads: Reads[NEL[Error]] = Reads {
case JsArray(errors) =>
errors.toList match {
case head :: tail =>
NEL(
head.validate[Error],
tail.map(_.validate[Error])
).sequence[JsResult, Error]
case Nil => JsError("expected a NonEmptyList but got empty list")
}
case other: JsValue =>
JsError(s"expected an array but got ${other.toString}")
}
implicit val nelErrorWrites: Writes[NEL[Error]] = Writes { nelErrors =>
Json.toJson(nelErrors.toList)
}
}
final case class ErrorResponse(errors: NEL[Error])
object ErrorResponse {
def apply(code: String, desc: String): ErrorResponse =
ErrorResponse(NEL.of(Error(code, desc)))
implicit val errorResponseFormat: OFormat[ErrorResponse] =
Json.format[ErrorResponse]
}
object Usage {
val nelToJsonString =
"""
|{
| "errors": {
| "head": {
| "code": "someCode",
| "description": "someDesc"
| },
| "tail": []
| }
|}
""".stripMargin
val er = ErrorResponse("someCode", "someDesc")
val erJSon = Json.toJson(er)
val erJsonString =
"""
|{
| "errors": [
| {
| "code": "someCode",
| "description": "someDesc"
| }
| ]
|}
""".stripMargin
val er2 = ErrorResponse(
NEL.of(
Error("someCode", "someDesc"),
Error("someCode2", "someDesc2")
)
)
val er2Json = Json.toJson(er2)
val er2JsonString = """
|{
| "errors": [
| {
| "code": "someCode",
| "description": "someDesc"
| },
| {
| "code": "someCode2",
| "description": "someDesc2"
| }
| ]
|}
""".stripMargin
}
import cats.Applicative
import cats.data.{NonEmptyList => NEL}
import cats.syntax.traverse._
import play.api.libs.json._
object NonEmptyListOps {
implicit object jsResultApplicative extends Applicative[JsResult] {
override def pure[A](x: A): JsResult[A] =
JsResult.applicativeJsResult.pure(x)
override def ap[A, B](
ff: JsResult[(A) => B]
)(fa: JsResult[A]): JsResult[B] =
JsResult.applicativeJsResult.apply(ff, fa)
}
def reads[T: Reads]: Reads[NEL[T]] = Reads {
case JsArray(elems) =>
elems.toList match {
case head :: tail =>
NEL(
head.validate[T],
tail.map(_.validate[T])
).sequence[JsResult, T]
case Nil => JsError("expected a NonEmptyList but got empty list")
}
case other: JsValue =>
JsError(s"expected an array but got ${other.toString}")
}
def writes[T: Writes]: Writes[NEL[T]] = Writes { elems =>
Json.toJson(elems.toList)
}
def format[T: Format]: Format[NEL[T]] = Format(reads[T], writes[T])
}
import cats.data.{NonEmptyList => NEL}
import play.api.libs.functional.syntax._
import play.api.libs.json._
object NonEmptyListOps {
def reads[T: Reads]: Reads[NEL[T]] =
Reads
.of[List[T]]
.collect(
JsonValidationError("expected a NonEmptyList but got an empty list")
) {
case head :: tail => NEL(head, tail)
}
def writes[T: Writes]: Writes[NEL[T]] =
Writes
.of[List[T]]
.contramap(_.toList)
def format[T: Format]: Format[NEL[T]] =
Format(reads, writes)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment