{"id":18828636,"url":"https://github.com/bizzabo/play-json-extensions","last_synced_at":"2025-04-07T17:11:32.034Z","repository":{"id":29349666,"uuid":"32883857","full_name":"bizzabo/play-json-extensions","owner":"bizzabo","description":"+22 field case class formatter and more for play-json","archived":false,"fork":false,"pushed_at":"2024-02-14T12:55:15.000Z","size":154,"stargazers_count":196,"open_issues_count":49,"forks_count":48,"subscribers_count":40,"default_branch":"master","last_synced_at":"2025-03-31T14:14:32.365Z","etag":null,"topics":["backend","rnd","xdotai"],"latest_commit_sha":null,"homepage":"http://cvogt.org/play-json-extensions/api/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bizzabo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-25T18:41:20.000Z","updated_at":"2024-12-09T19:53:14.000Z","dependencies_parsed_at":"2024-11-15T07:04:47.274Z","dependency_job_id":"cd85c4b3-ae60-45ac-aa96-328e5e99edf9","html_url":"https://github.com/bizzabo/play-json-extensions","commit_stats":null,"previous_names":["xdotai/play-json-extensions"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizzabo%2Fplay-json-extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizzabo%2Fplay-json-extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizzabo%2Fplay-json-extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizzabo%2Fplay-json-extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bizzabo","download_url":"https://codeload.github.com/bizzabo/play-json-extensions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694877,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backend","rnd","xdotai"],"created_at":"2024-11-08T01:33:58.225Z","updated_at":"2025-04-07T17:11:31.990Z","avatar_url":"https://github.com/bizzabo.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"Play-Json extensions\n==========================\n\n### installation\n\nAdd this to your `build.sbt`:\n\n```\nlibraryDependencies += \"ai.x\" %% \"play-json-extensions\" % \"0.42.0\"\n```\n\n### latest versions\n\n| Play-json | Scala            | group id    | latest version       |\n| --------- | ---------------- | ------------| -------------------- |\n| 2.8.x     | 2.12, 2.13       | `ai.x`      | 0.42.0               |\n| 2.7.x     | 2.11, 2.12, 2.13 | `ai.x`      | 0.40.2               |\n| 2.5.x     | 2.11             | `ai.x`      | 0.9.0                |\n| 2.4.x     | 2.11             | `org.cvogt` | 0.6.1                |\n| 2.3.x     | 2.11             | `org.cvogt` | 0.2                  |     \n\n### all versions and scaladoc\n\n[0.40 - latest](https://www.javadoc.io/doc/ai.x/play-json-extensions_2.13/)\n\n[0.9 - 0.30](https://www.javadoc.io/doc/ai.x/play-json-extensions_2.11/)\n\n[0.1 - 0.8](https://www.javadoc.io/doc/org.cvogt/play-json-extensions_2.11/)\n\n\n### De-/Serialize case classes of arbitrary size (23+ fields allowed)\n\n```scala\n    case class Foo(\n      _1:Int,_2:Int,_3:Int,_4:Int,_5:Int,\n      _21:Int,_22:Int,_23:Int,_24:Int,_25:Int,\n      _31:Int,_32:Int,_33:Int,_34:Int,_35:Int,\n      _41:Int,_42:Int,_43:Int,_44:Int,_45:Int,\n      _51:Int,_52:Int,_53:Int,_54:Int,_55:Int\n    )\n\n    val foo = Foo(1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5)\n```\n\n#### Create explicit formatter\n\n```scala\n    import ai.x.play.json.Jsonx\n    implicit lazy val jsonFormat = Jsonx.formatCaseClass[Foo]\n\n    // if your case class uses Option make sure you import\n    // one of the below implicit Option Reads to avoid\n    // \"could not find implicit value for parameter helper: ai.x.play.json.OptionValidationDispatcher\"\n\n    // note: formatCaseClass catches IllegalArgumentException and turns them into JsError enclosing the stack trace as the message\n    // this allows using require(...) in class constructors and still get JsErrors out of serialization\n```\n\n#### Then use ordinary play-json\n\n```scala\n    val json = Json.toJson( foo )\n    assert(foo == json.as[Foo])\n```\n\n#### deserialization uses default values\n\n```scala\n    case class Bar(s: String, i: Int = 6)\n    implicit lazy val format = Jsonx.formatCaseClassUseDefaults[Bar]\n    assert(Bar(\"asd\",6) == Json.parse(\"\"\"{\"s\":\"asd\"}\"\"\").validate[Bar].get)\n```\n  \n#### De-/Serialize tuples\n\n```scala\n    import ai.x.play.json.tuples._\n    val json = Json.parse(\"\"\"[1,1.0,\"Test\"]\"\"\")\n    val res = Json.fromJson[(Int,Double,String)](json)\n    assert(JsSuccess((1,1.0,\"Test\")) === res)\n```\n\n#### De-/Serialize single value classes\n\n```scala\n    case class Foo(i: Int)\n    val json = Json.parse(\"1\")\n    val res = Json.fromJson[Foo](json)\n    assert(JsSuccess(Foo(1)) === res)\n```\n\n### Option for play-json 2.4\n\n#### implicit Option Reads\n\n```scala\n    import ai.x.play.json.implicits.optionWithNull // play 2.4 suggested behavior\n    // or\n    import ai.x.play.json.implicits.optionNoError // play 2.3 behavior\n```\n\n#### automatic option validation: `validateAuto`\n\n```scala\n    val json = (Json.parse(\"\"\"{}\"\"\") \\ \"s\")\n    json.validateAuto[Option[String]] == JsResult(None) // works as expected correctly\n\n    // play-json built-ins\n    json.validate[Option[String]] // JsError: \"'s' is undefined on object: {}\"\n    json.validateOpt[String] == JsResult(None) // manual alternative (provided here, built-into play-json \u003e= 2.4.2)\n```\n    \n#### automatic formatting of sealed traits, delegating to formatters of the subclasses\n#### formatSealed uses orElse of subclass Reads in random order, careful in case of ambiguities of field-class correspondances\n\n```scala\n    sealed trait SomeAdt\n    case object A extends SomeAdt\n    final case class X(i: Int, s: String) extends SomeAdt\n    object X{\n      implicit lazy val jsonFormat: Format[X] = Jsonx.formatCaseClass[X]\n    }\n    object SomeAdt{\n      import ai.x.play.json.SingletonEncoder.simpleName  // required for formatSingleton\n      import ai.x.play.json.implicits.formatSingleton    // required if trait has object children\n      implicit lazy val jsonFormat: Format[SomeAdt] = Jsonx.formatSealed[SomeAdt]\n    }\n\n    Json.parse(\"\"\"A\"\"\").as[SomeAdt] == A\n    Json.parse(\"\"\"{\"i\": 5, \"s\":\"foo\", \"type\": \"X\"}\"\"\").as[SomeAdt] == X(5,\"foo\")\n```\n\n#### formatSealedWithFallback[A,B \u003c: A] is like formatSealed but provides a fallback for unknown cases.\n#### It makes sure the class B is tried last, which allows it to be permissive enough for a fallback.\n#### This allows graceful schema evolution without breaking old readers. Example:\n\n```scala\n    sealed trait SomeAdt\n    final case class X(i: Int, s: String) extends SomeAdt\n    final case class Unknown(json: JsValue) extends SomeAdt\n    object SomeAdt{\n      implicit lazy val jsonFormat: Format[SomeAdt] = {\n        implicit lazy val XFormat: Format[X] = Jsonx.formatCaseClass[X]\n        implicit lazy val UnknownFormat: Format[Unknown] = Jsonx.formatInline[Unknown]\n        Jsonx.formatSealedWithFallback[SomeAdt]\n      }\n    }\n\n    Json.parse(\"\"\"{\"i\": 5, \"s\":\"foo\", \"type\": \"X\"}\"\"\").as[SomeAdt] == X(5,\"foo\")\n    val unknownJson = Json.parse(\"\"\"{\"x\":\"y\"}\"\"\")\n    unknownJson.as[Unknown] == Unknown(unknownJson)\n```\n\n#### formatSealedWithFallback[A,B \u003c: A] is like formatSealed but provides a fallback for unknown cases.\n#### It makes sure the class B is tried last, which allows it to be permissive enough for a fallback.\n#### This allows graceful schema evolution without breaking old readers. Example:\n\n```scala\n    sealed trait SomeAdt\n    final case class X(i: Int, s: String) extends SomeAdt\n    final case class Unknown(json: JsValue) extends SomeAdt\n    object SomeAdt{\n      implicit lazy val jsonFormat: Format[SomeAdt] = {\n        implicit lazy val XFormat: Format[X] = Jsonx.formatCaseClass[X]\n        implicit lazy val UnknownFormat: Format[Unknown] = Jsonx.formatInline[Unknown]\n        Jsonx.formatSealedWithFallback[SomeAdt]\n      }\n    }\n\n    Json.parse(\"\"\"{\"i\": 5, \"s\":\"foo\", \"type\": \"X\"}\"\"\").as[SomeAdt] == X(5,\"foo\")\n    val unknownJson = Json.parse(\"\"\"{\"x\":\"y\"}\"\"\")\n    unknownJson.as[Unknown] == Unknown(unknownJson)\n```\n\n### experimental features (will change)\n#### Serialization nirvana - formatAuto FULLY automatic de-serializer (note: needs more optimized internal implementation)\n\n```scala\n    sealed trait SomeAdt\n    case object A extends SomeAdt\n    final case class X(i: Int, s: String) extends SomeAdt\n    object Baz\n    case class Bar(a: Int, b:Float, foo: Baz.type, o: Option[Int])\n    case class Foo(_1:Bar,_11:SomeAdt, _2:String,_3:Int,_4:Int,_5:Int,_21:Int,_22:Int,_23:Int,_24:Int,_25:Int,_31:Int,_32:Int,_33:Int,_34:Int,_35:Int,_41:Int,_42:Int,_43:Int,_44:Int,_45:Int,_51:Int,_52:Int,_53:Int,_54:Int,_55:Int)\n    val foo = Foo(Bar(5,1.0f, Baz, Some(4): Option[Int]),A,\"sdf\",3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5)\n    val foo2 = Foo(Bar(5,1.0f, Baz, None: Option[Int]),X(5,\"x\"),\"sdf\",3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5)\n    \n    import ai.x.play.json.implicits.optionWithNull\n    val fmt2: Format[Foo] = Jsonx.formatAuto[Foo] // not implicit to avoid infinite recursion\n\n    {\n      implicit lazy val fmt3: Format[Foo] = fmt2    \n      val json = Json.toJson( foo )\n      assert(foo === json.as[Foo])\n      val json2 = Json.toJson( foo2 )\n      assert(foo2 === json2.as[Foo])\n    }\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbizzabo%2Fplay-json-extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbizzabo%2Fplay-json-extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbizzabo%2Fplay-json-extensions/lists"}