{"id":13801550,"url":"https://github.com/battermann/sbt-json","last_synced_at":"2025-04-30T05:45:30.179Z","repository":{"id":86688958,"uuid":"100510206","full_name":"battermann/sbt-json","owner":"battermann","description":"sbt plugin that generates Scala case classes for easy, statically typed and implicit access of JSON data e.g. from API responses","archived":false,"fork":false,"pushed_at":"2018-05-29T20:34:36.000Z","size":132,"stargazers_count":31,"open_issues_count":1,"forks_count":4,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-04-30T05:45:25.055Z","etag":null,"topics":["code-generation","json","sbt","sbt-plugin","scala"],"latest_commit_sha":null,"homepage":"http://blog.leifbattermann.de/2017/08/17/modelling-api-responses-with-sbt-json-print-current-bitcoin-price/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/battermann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-08-16T16:28:39.000Z","updated_at":"2024-03-17T22:44:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"acff4b31-5ed6-45e4-b48d-a095357be02a","html_url":"https://github.com/battermann/sbt-json","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/battermann%2Fsbt-json","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/battermann%2Fsbt-json/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/battermann%2Fsbt-json/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/battermann%2Fsbt-json/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/battermann","download_url":"https://codeload.github.com/battermann/sbt-json/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251651221,"owners_count":21621702,"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":["code-generation","json","sbt","sbt-plugin","scala"],"created_at":"2024-08-04T00:01:24.224Z","updated_at":"2025-04-30T05:45:30.158Z","avatar_url":"https://github.com/battermann.png","language":"Scala","readme":"# sbt-json\n\n\u003c!-- TOC --\u003e\n\n- [Overview](#overview)\n- [Prerequisites](#prerequisites)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Why use sbt-json?](#why-use-sbt-json)\n- [Settings](#settings)\n- [Examples](#example)\n- [Settings in depth](#settings-in-depth)\n- [Code generation features](#code-generation-features)\n- [Tasks](#tasks)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n\n\u003c!-- /TOC --\u003e\n\n## Overview\n\nsbt-json is an sbt plugin that generates Scala case classes for easy, statically typed, and implicit access of JSON data e.g. from API responses.\n\nThe plugin makes it possible to access JSON documents in a statically typed way including auto-completion. It takes a sample JSON document as input (either from a file or a URL) and generates Scala types that can be used to read data with the same structure.\n\nsbt-json integrates very well with the [play-json library](https://github.com/playframework/play-json) as it can also generate play-json formats for implicit conversion of a `JsValue` to its Scala representation. (see [example](https://github.com/battermann/sbt-json/blob/master/README.md#play-json))\n\nsbt-json also works with [circe](https://circe.github.io/circe/) for many JSON schemas as circe automatically derives the necessary type classes for the generated types. (see [example](https://github.com/battermann/sbt-json/blob/master/README.md#circe))\n\nSupporting the generation of implicit encoders and decoders for different JSON libraries other than play-json is planned for future versions.\n\n## Prerequisites\n\n0.13.5 \u003c= sbt version\n\n## Installation\n\nInstall the plugin according to the [sbt documentation](http://www.scala-sbt.org/0.13/docs/Using-Plugins.html).\n\n### Edit `project/plugins.sbt`\n\n    addSbtPlugin(\"com.github.battermann\" % \"sbt-json\" % \"0.5.0\")\n\n### Edit `build.sbt`\n\nEdit the `build.sbt` file to enable the plugin and to generate case class sources whenever the compile task is executed:\n\n    enablePlugins(SbtJsonPlugin)\n    \n#### Optional JSON library\n\nIf you want to use play-json e.g. add:\n\n    libraryDependencies += \"com.typesafe.play\" %% \"play-json\" % \"2.6.0\"\n    \n## Usage\n\nAfter a successful installation place one or more `.json` files containing sample JSON documents in the directory `src/main/resources/json/`.\n\nBy default only the case classes will be generated. To generate play-json formats, add `jsonInterpreter := plainCaseClasses.withPlayJsonFormats` to your `build.sbt` file and reload sbt.\n\nOn compile, case classes will be generated in `target/scala-{version}/src_managed/compiled_json/jsonmodels/{name}` where `name` will be the name of the corresponding `.json` file.\n\nTo use the generated models, import `jsonmodels.{name}._` in your application code. You can now map a JSON document that has the same schema as the sample JSON document (e.g. from an API response) to the generated models. This can be done implicitly e.g. with circe or with play-json (see examples below).\n\n## Why use sbt-json?\n\nsbt-json supports easy, statically typed and implicit access to JSON data with minimal overhead and minimal boiler-plate.\n\nThere are other online tools (e.g. [http://json2caseclass.cleverapps.io](http://json2caseclass.cleverapps.io) or [http://transform.now.sh/json-to-scala-case-class](https://transform.now.sh/json-to-scala-case-class)) that allow pasting a JSON string to generate Scala case classes which you can copy and paste back into your solution. But with sbt-json, once installed, you don't need an external tool. Moreover, the workflow of adding a new JSON schema involves less steps and the generated case classes do not need to be maintained.\n\nAdditionally sbt-json handles a lot of edge cases that will cause problems when using the available online tools. For example:\n\n* Optional object fields will be recognized automatically for an array of a given objects\n* Derived class names will be unique\n* Scala reserved words will be avoided\n\nAnother advantage of sbt-json is the optional generation of play-json formats that otherwise you would have to write manually.\n\n## Settings\n\n| name     | default | description |\n| -------- | ------- | ----------- |\n| jsonInterpreter | `plainCaseClasses`    | Combinator that specifies which interpreter to use. (`plainCaseClasses` can be combined with `withPlayJsonFormats`: `plainCaseClasses.withPlayJsonFormats `) |\n| jsValueFilter     | `allJsValues`    | Combinator that specifies which JSON values should be in-/excluded for analyzation. (`allJsValues` can be combined with `exceptEmptyArrays` and `exceptNullValues`. Example: `allJsValues.exceptEmptyArrays` |\n| jsonSourcesDirectory  | `src/main/resources/json` | Path containing the `.json` files to analyze. |\n| jsonUrls  | `Nil` | List of urls that serve JSON data to be analyzed. |\n| jsonOptionals | `Nil` | Specify which fields should be optional, e.g. `jsonOptionals := Seq(OptionalField(\"\u003cpackage_name\u003e\", \"\u003cclass_name\u003e\", \"\u003cfield_name\u003e\"))` |\n| packageNameForJsonModels | `jsonmodels` | Package name for the generated case classes. |\n\n## Example\n\n### play-json\n\nIf you want to analyze JSON data form `https://www.bing.com/HPImageArchive.aspx?format=js\u0026idx=0\u0026n=1\u0026mkt=en-US` and ignore empty arrays, add the following lines to the `build.sbt` file:\n\n    jsonInterpreter := plainCaseClasses.withPlayJsonFormats\n    jsonUrls += \"https://www.bing.com/HPImageArchive.aspx?format=js\u0026idx=0\u0026n=1\u0026mkt=en-US\"\n    jsValueFilter := allJsValues.exceptEmptyArrays\n\nThen use play-json to read the JSON data:\n\n    import play.api.libs.json.Json\n    import jsonmodels.hpimagearchive._\n\n    val json = Source.fromURL(\"https://www.bing.com/HPImageArchive.aspx?format=js\u0026idx=0\u0026n=1\u0026mkt=en-US\").mkString\n    val imageArchive = Json.parse(json).as[HPImageArchive]\n    println(imageArchive.images.head.url)\n\n### circe\n\nsbt-json also works with [circe](https://circe.github.io/circe/) for many JSON schemas as circe automatically derives the necessary type classes for the generated types.\n\nIn the `buld.sbt` add the circe dependencies:\n\n    val circeVersion = \"0.8.0\"\n\n    libraryDependencies ++= Seq(\n      \"io.circe\" %% \"circe-core\",\n      \"io.circe\" %% \"circe-generic\",\n      \"io.circe\" %% \"circe-parser\"\n    ).map(_ % circeVersion)\n    \nNow add a file or URL with the JSON sample, e.g.:\n\n    jsonUrls += \"https://api.coindesk.com/v1/bpi/currentprice.json\"\n    \nUse circe to decode the JSON data:\n\n    import io.circe.generic.auto._\n    import io.circe.parser._\n    import jsonmodels.currentprice._      \n    \n    val url = \"https://api.coindesk.com/v1/bpi/currentprice.json\"\n    val rawJson = scala.io.Source.fromURL(url).mkString\n    val currentPriceOrError = decode[Currentprice](rawJson)\n    val output = currentPriceOrError fold (\n      err =\u003e err.getMessage,\n      currentPrice =\u003e {\n        val info = currentPrice.bpi.EUR.description\n        val priceInEuro = currentPrice.bpi.EUR.rate_float\n        val date = currentPrice.time.updated\n        s\"Current Bitcoin price ($info): $priceInEuro (timestamp: $date)\"\n      }\n    )\n\n    println(output)\n\n## Settings in depth\n\n### jsonInterpreter\n\nWith the `jsonInterpreter` setting additional generation features can be configured.\n\nBesides generating the case classes, we can specify to generate [play-json formats](https://www.playframework.com/documentation/2.6.x/ScalaJsonCombinators#Format) for implicit conversion.\n\nThe interpreters can be set like this in the `build.sbt` file (which is the default):\n\n    jsonInterpreter := plainCaseClasses\n    \nor if play-json-formats should be generated:\n\n    jsonInterpreter := plainCaseClasses.withPlayJsonFormats\n\n### jsValueFilter\n\nBy default the code generation will fail if the JSON sample contains empty arrays or null values. This follows the fail fast paradigm because some type information might be missing.\n\nTo change this behavior you can set `jsValueFilter` to ignore empty arrays or null values. The type of this setting is `type JsValueFilter = JsValue =\u003e Boolean` and there are two combinators available as well as a convenient syntax (implicit classes).\n\nConfigure this setting to ignore empty arrays:\n\n    jsValueFilter := allJsValues.exceptEmptyArrays\n\nIgnore null values:\n\n    jsValueFilter := allJsValues.exceptNullValues\n\nIgnore empty arrays as well as null values:\n\n    jsValueFilter := allJsValues.exceptEmptyArrays.exceptNullValues\n\n### jsonSourcesDirectory\n\nBy default all files with a `.json` extension in the directory `src/main/resources/json` will be analyzed. To change the directory set `jsonSourcesDirectory` of type `jva.io.File` to the desired value, e.g.:\n\n    jsonSourcesDirectory := baseDirectory.value / \"json\"\n\n### jsonUrls\n\n`jsonUrls` is a sequence of strings that represent URLs that serve JSON documents to be analyzed. Add a new URL like this:\n\n    jsonUrls += \"https://www.bing.com/HPImageArchive.aspx?format=js\u0026idx=0\u0026n=1\u0026mkt=en-US\"\n\n### jsonOptionals\n\nIf the JSON documents contain optional fields, they have to be explicitly marked as such. To do this, add a value of type `OptionalField` containing the package name, class name, and field name to the `jsonOptionals` setting.\n\n#### Example\n\nPlace a file `fbpost.json` containng a JSON document of a facebook post inside the json-sources directory:\n\n    {\n        \"id\":\"339880699398622_241628669274112\",\n        \"created_time\":\"2012-06-19T07:51:06+0000\",\n        \"message\":\"great information for Linux fan ;-)\",\n        \"full_picture\":\"https:\\/\\/scontent.xx.fbcdn.net\\/v\\/t31.0-8\\/s720x720\\/177827_10151014484731203_401775304_o.jpg?oh=69db574b81ebe6bfe97a4077b6806775\u0026oe=5A25DA17\"\n    }\n\nYou can inspect the result of the code generation by running the sbt-json task `printJsonModels`:\n\n    [info] /** MACHINE-GENERATED CODE. DO NOT EDIT DIRECTLY */\n    [info] package jsonmodels.fbpost\n    [info] \n    [info] case class Fbpost(\n    [info]   id: String,\n    [info]   created_time: String,\n    [info]   message: String,\n    [info]   full_picture: String\n    [info] )\n    [info] \n    [info] object Fbpost {\n    [info]   import play.api.libs.json.Json\n    [info] \n    [info]   implicit val formatFbpost = Json.format[Fbpost]\n    [info] }\n\nHere the type of the `message` field is `String`. However, some facebook posts do not contain a message field. Implicit decoding of a JSON document like this will fail:\n\n    {\n        \"id\":\"339880699398622_371821532871205\",\n        \"created_time\":\"2012-06-19T07:57:54+0000\",\n        \"full_picture\":\"https:\\/\\/scontent.xx.fbcdn.net\\/v\\/t31.0-8\\/s720x720\\/469692_371821072871251_145095902_o.jpg?oh=8a1be9485002e2d25dbe396a8f1fe176\u0026oe=5A2F45F8\"\n    }\n\nTo fix this, the field has to be marked as optional. Add the following line to the `build.sbt` file:\n\n    jsonOptionals += OptionalField(\"fbpost\", \"Fbpost\", \"message\")\n\nRun `reload` in the sbt console and inspect the generated code again with `printJsonModels`. The `message` field is now of type `Option[String]`:\n\n    [info] /** MACHINE-GENERATED CODE. DO NOT EDIT DIRECTLY */\n    [info] package jsonmodels.fbpost\n    [info] \n    [info] case class Fbpost(\n    [info]   id: String,\n    [info]   created_time: String,\n    [info]   message: Option[String],\n    [info]   full_picture: String\n    [info] )\n    [info] \n    [info] object Fbpost {\n    [info]   import play.api.libs.json.Json\n    [info] \n    [info]   implicit val formatFbpost = Json.format[Fbpost]\n    [info] }\n\n## Code generation features\n\n### Unification of array types of similar JSON objects\n\nIf the JSON objects of an array are not consistent, the generator will unify the type by making all fields optional that are not shared by all objects.\n\n#### Example\n\nThe fields `message` and `full_picture` of the generated case class will be optional for this sample JSON document:\n\n    {\n        \"posts\":{\n            \"data\":[\n            {\n                \"id\":\"339880699398622_185311264930535\",\n                \"created_time\":\"2012-06-06T08:31:09+0000\"\n            }\n            {\n                \"id\":\"339880699398622_347136625339696\",\n                \"created_time\":\"2012-05-10T06:42:49+0000\",\n                \"message\":\"Functional Programming and Android Game Development, a Happy Couple\"\n            },\n            {\n                \"id\":\"339880699398622_403676613005660\",\n                \"created_time\":\"2012-05-09T21:24:31+0000\",\n                \"message\":\"an HTML5 version of the popular Cut the Rope  game with 25 levels is now available online for free\\nhttp:\\/\\/www.cuttherope.ie\\/\",\n                \"full_picture\":\"https:\\/\\/external.xx.fbcdn.net\\/safe_image.php?d=AQC2A2jO1N9RhI2g\u0026url=http\\u00253A\\u00252F\\u00252Fwww.cuttherope.ie\\u00252Ffb.png\u0026_nc_hash=AQClKYpmQtwavvP0\"\n            }\n            ],\n        \"id\":\"339880699398622\"\n    }\n\nGenerated case classes:\n\n    /** MACHINE-GENERATED CODE. DO NOT EDIT DIRECTLY */\n    package jsonmodels.facebook\n\n    case class Facebook(\n        posts: Posts,\n        id: String\n    )\n\n    case class Posts(\n        data: Seq[Data]\n    )\n\n    case class Data(\n        id: String,\n        created_time: String,\n        full_picture: Option[String],\n        message: Option[String]\n    )\n\n### Ensure unique names\n\nThe generator will search for equal class names which can not be defined within the same scope and append ascending numbers starting with 1.\n\n#### Example\n\nConsider this JSON document:\n\n    {\n        \"value1\": {\n            \"foo\": { \"value\": 42 }\n        },\n        \"value2\": {\n            \"foo\": { \"value\": \"some string\" }\n        }\n    }\n\nThe fields of the objects `value1` and `value2` have the same name (`foo`) but different types. Therefore the generated class names will be `Foo` and `Foo1`:\n\n    case class Equalnames(\n        value1: Value1,\n        value2: Value2\n    )\n\n    case class Value1(\n        foo: Foo  \n    )\n\n    case class Foo(\n        value: Double\n    )\n\n    case class Value2(\n        foo: Foo1\n    )\n\n    case class Foo1(\n        value: String\n    )\n\n### Avoid Scala reserved words and type names\n\nThe generator tries to avoid Scala reserved words and type names by appending the suffix `Model` to a class name that is a potential candidate to clash. E.g. `case class List()` will become `case class ListModel()`, or `case class MyClass(this: String)` will become ``case class MyClass(`this`: String)``. (This feature is not yet fully tested and there is still no guaranty that there won't be any clashes.)\n\n### Unify type with exact same structure\n\nIf an objects schema has the exact same structure as a schema that was found before, it will be substituted by that schema.\n\n    {\n        \"geometry\": {\n            \"location\": {\n                \"lat\": 37.42291810,\n                \"lng\": -122.08542120\n            },\n            \"viewport\": {\n                \"northeast\": {\n                    \"lat\": 37.42426708029149,\n                    \"lng\": -122.0840722197085\n                },\n                \"southwest\": {\n                    \"lat\": 37.42156911970850,\n                    \"lng\": -122.0867701802915\n                }\n            }\n        }\n    }\n\nNote that there are no case classes `Northeast` and `Southwest` generated. Instead the type of the fields will be declared as `Location`:\n\n    case class Geo(\n        geometry: Geometry\n    )\n\n    case class Geometry(\n        location: Location,\n        viewport: Viewport\n    )\n\n    case class Location(\n        lat: Double,\n        lng: Double\n    )\n\n    case class Viewport(\n        northeast: Location,\n        southwest: Location\n    )\n\n## Tasks\n\n| name     | description |\n| -------- | ----------- |\n| printJsonModels | Prints the generated case classes to the console. |\n| generateJsonModels | Creates source files containing the generates case classes. |\n\n## Troubleshooting\n\n### Why is the generated code ignored by IntelliJ?\n\nIf the generated code is ignored by the compiler, in the **Project** tool window include the folder `target/scala-{version}/src_managed` by selecting **Mark Directory as | Generated Source Root** through the context menu, as described [here](https://www.jetbrains.com/help/idea/configuring-content-roots.html#d85322e277).\n\n## Contributing\n\nContributions are very welcome and highly appreciated. You can contribute by sending pull request, by reporting bugs and feature requests [here](https://github.com/battermann/sbt-json/issues), or by giving feedback and suggestions for improvement.\n","funding_links":[],"categories":["Table of Contents","JSON"],"sub_categories":["JSON"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbattermann%2Fsbt-json","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbattermann%2Fsbt-json","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbattermann%2Fsbt-json/lists"}