Created
September 22, 2025 12:45
-
-
Save oscarduignan/1bd9ebdd0654a2f7e9664a8913954c63 to your computer and use it in GitHub Desktop.
Example of the stuff I was saying about "explicitly"? mapping rather than using functional combinators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //> using scala 2.13 | |
| //> using dep org.playframework::play-json:3.0.5 | |
| //> using dep com.lihaoyi::pprint:0.9.3 | |
| import play.api.libs.json._ | |
| sealed trait Content | |
| case object Empty extends Content | |
| case class Text(value: String) extends Content | |
| case class HtmlContent(value: String) extends Content | |
| object Content { | |
| implicit val formatContent: Format[Content] = new Format[Content] { | |
| def reads(json: JsValue): JsResult[Content] = json match { | |
| case JsObject(values) => | |
| values.get("html").map({ | |
| case JsString(s) => JsSuccess(HtmlContent(s)) | |
| case _ => JsError(JsPath \ "html", "Expected html to be string") | |
| }).orElse(values.get("text").map({ | |
| case JsString(s) => JsSuccess(Text(s)) | |
| case _ => JsError(JsPath \ "text", "Expected text to be string") | |
| })).getOrElse( | |
| JsError("Expected object with html or text key") | |
| ) | |
| case _ => | |
| JsError("Expected object with html or text key") | |
| } | |
| def writes(content: Content): JsValue = content match { | |
| case Empty => JsNull | |
| case Text(value) => JsObject(Map("text" -> JsString(value))) | |
| case HtmlContent(value) => JsObject(Map("html" -> JsString(value))) | |
| } | |
| } | |
| } | |
| case class ServiceNavigationSlot( | |
| end: Content = Empty, | |
| start: Content = Empty, | |
| navigationEnd: Content = Empty, | |
| navigationStart: Content = Empty | |
| ) | |
| object ServiceNavigationSlot { | |
| implicit private val formatAlwaysHtmlContent: Format[Content] = new Format[Content] { | |
| def reads(json: JsValue): JsResult[Content] = json match { | |
| case JsNull => JsSuccess(Empty) | |
| case JsString(s) => JsSuccess(HtmlContent(s)) | |
| case _ => JsError("Expected string or null") | |
| } | |
| def writes(content: Content): JsValue = content match { | |
| case Empty => JsNull | |
| case Text(value) => JsString(value) | |
| case HtmlContent(value) => JsString(value) | |
| } | |
| } | |
| // Json.using[Json.WithDefaultValues] isn't needed in scala 3 - not sure exactly why it is in scala 2.13 | |
| implicit val formatServiceNavigationSlot: Format[ServiceNavigationSlot] = | |
| Json.using[Json.WithDefaultValues].format[ServiceNavigationSlot] | |
| } | |
| object Example extends App { | |
| pprint.pprintln(Json.parse( | |
| """ | |
| |{ | |
| | "end": "test", | |
| | "start": "test", | |
| | "navigationEnd": "test" | |
| |}""".stripMargin).as[ServiceNavigationSlot]) | |
| pprint.pprintln(Json.parse( | |
| """ | |
| |{ "text": "test" } | |
| |""".stripMargin).as[Content]) | |
| pprint.pprintln(Json.parse( | |
| """ | |
| |{ "html": "<p>test</p>" } | |
| |""".stripMargin).validate[Content]) | |
| pprint.pprintln(Json.parse( | |
| """ | |
| |{ "html": "<p>test</p>", "text": "test" } | |
| |""".stripMargin).validate[Content]) | |
| pprint.pprintln(Json.parse( | |
| """ | |
| |{ "html": 123 } | |
| |""".stripMargin).validate[Content]) | |
| pprint.pprintln(Json.stringify(Json.toJson(ServiceNavigationSlot( | |
| start = HtmlContent("<p>test</p>"), | |
| end = Text("test") | |
| )))) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment