circe is a JSON library for Scala (and [Scala.js][scala-js]). The rest of this page tries to give some justification for its existence. There are also [API docs][api].
circe's working title was jfc, which stood for "JSON for [cats][cats]". The name was changed for a number of reasons.
- Quick start
- Why?
- Dependencies and modularity
- Parsing
- Lenses
- Codec derivation
- Aliases
- Documentation
- Testing
- Performance
- Usage
- Encoding and decoding
- Transforming JSON
- Contributors and participation
- Warnings and known issues
- License
circe is published to [Maven Central][maven-central] and cross-built for Scala 2.10 and 2.11, so you can just add the following to your build:
libraryDependencies ++= Seq(
"io.circe" %% "circe-core" % "0.3.0",
"io.circe" %% "circe-generic" % "0.3.0",
"io.circe" %% "circe-parser" % "0.3.0"
)
If you are using circe's generic derivation with Scala 2.10, you'll also need to include the [Macro Paradise][paradise] compiler plugin in your build:
addCompilerPlugin(
"org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full
)
Then type sbt console
to start a REPL and then paste the following (this will also work from the
root directory of this repository):
scala> import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
scala> sealed trait Foo
defined trait Foo
scala> case class Bar(xs: List[String]) extends Foo
defined class Bar
scala> case class Qux(i: Int, d: Option[Double]) extends Foo
defined class Qux
scala> val foo: Foo = Qux(13, Some(14.0))
foo: Foo = Qux(13,Some(14.0))
scala> foo.asJson.noSpaces
res0: String = {"Qux":{"d":14.0,"i":13}}
scala> decode[Foo](foo.asJson.spaces4)
res1: cats.data.Xor[io.circe.Error,Foo] = Right(Qux(13,Some(14.0)))
No boilerplate, no runtime reflection.
[Argonaut][argonaut] is a great library. It's by far the best JSON library for Scala, and the best JSON library on the JVM. If you're doing anything with JSON in Scala, you should be using Argonaut.
circe is a fork of Argonaut with a few important differences.
circe depends on [cats][cats] instead of [Scalaz][scalaz], and the core
project has only two
dependencies: cats-core and [export-hook][export-hook] (a lightweight mechanism for cleaner generic
type class instance derivation).
Other subprojects bring in dependencies on [Jawn][jawn] (for parsing in the [jawn
][circe-jawn]
subproject), [Shapeless][shapeless] (for automatic codec derivation in [generic
][circe-generic]),
and [Twitter Util][util] (for tools for asynchronous parsing in async
), but it would be possible
to replace the functionality provided by these subprojects with alternative implementations that use
other libraries.
circe doesn't include a JSON parser in the core
project, which is focused on the JSON AST, zippers,
and codecs. The [jawn
][circe-jawn] subproject provides support for parsing JSON via a [Jawn][jawn]
facade. Jawn is fast, it offers asynchronous parsing, and best of all it lets us drop a lot of the
fussiest code in Argonaut. The [jackson
][circe-jackson] subproject supports using
[Jackson][jackson] for both parsing and printing.
circe also provides a [parser
][circe-parser] subproject that provides parsing support for Scala.js,
with JVM parsing provided by io.circe.jawn
and JavaScript parsing from scalajs.js.JSON
.
circe doesn't use or provide lenses in the core
project. This is related to the first point above,
since [Monocle][monocle] has a Scalaz dependency, but we also feel that it simplifies the API. The
0.3.0 release adds [an experimental optics
subproject][optics-pr] that provides Monocle lenses
(note that this will require your project to depend on both Scalaz and cats).
circe does not use macros or provide any kind of automatic derivation in the core
project. Instead
of Argonaut's limited macro-based derivation (which does not support sealed trait hierarchies, for
example), circe includes a subproject (generic
) that provides generic codec derivation using
[Shapeless][shapeless].
[This subproject][circe-generic] is currently a simplified port of [argonaut-shapeless][argonaut-shapeless] that provides fully automatic derivation of instances for case classes and sealed trait hierarchies. It also includes derivation of "incomplete" case class instances (see my recent [blog post][incompletes] for details).
circe aims to simplify Argonaut's API by removing all operator aliases. This is largely a matter of personal taste, and may change in the future.
The Argonaut documentation is good, but it could be better: to take just one example, it can be hard
to tell at a glance why there are three different Cursor
, HCursor
, and ACursor
types. In this
particular case, circe introduces an abstraction over cursors that makes the relationship clearer and
allows these three types to [share API documentation][generic-cursor].
I'd like to provide more complete test coverage (in part via [Discipline][discipline]), but it's early days for this.
circe aims to be more focused on performance. I'm still experimenting with the right balance, but I'm open to using mutability, inheritance, and all kinds of other horrible things under the hood if they make circe faster (the public API does not and will never expose any of this, though).
[My initial benchmarks][benchmarks] suggest this is at least kind of working (higher numbers are better):
Benchmark Mode Cnt Score Error Units
DecodingBenchmark.decodeFoosC thrpt 40 3711.680 ± 22.766 ops/s
DecodingBenchmark.decodeFoosA thrpt 40 1519.045 ± 11.373 ops/s
DecodingBenchmark.decodeFoosP thrpt 40 2032.834 ± 27.033 ops/s
DecodingBenchmark.decodeFoosPico thrpt 40 2003.106 ± 10.463 ops/s
DecodingBenchmark.decodeFoosS thrpt 40 7053.699 ± 35.127 ops/s
DecodingBenchmark.decodeIntsC thrpt 40 19101.875 ± 324.123 ops/s
DecodingBenchmark.decodeIntsA thrpt 40 8000.093 ± 215.702 ops/s
DecodingBenchmark.decodeIntsP thrpt 40 18160.031 ± 68.777 ops/s
DecodingBenchmark.decodeIntsPico thrpt 40 11979.085 ± 89.793 ops/s
DecodingBenchmark.decodeIntsS thrpt 40 81279.228 ± 1203.751 ops/s
EncodingBenchmark.encodeFoosC thrpt 40 7353.158 ± 133.633 ops/s
EncodingBenchmark.encodeFoosA thrpt 40 5638.358 ± 30.315 ops/s
EncodingBenchmark.encodeFoosP thrpt 40 2324.075 ± 17.868 ops/s
EncodingBenchmark.encodeFoosPico thrpt 40 5056.317 ± 45.876 ops/s
EncodingBenchmark.encodeFoosS thrpt 40 5307.422 ± 29.666 ops/s
EncodingBenchmark.encodeIntsC thrpt 40 117885.093 ± 2151.059 ops/s
EncodingBenchmark.encodeIntsA thrpt 40 72986.276 ± 1561.295 ops/s
EncodingBenchmark.encodeIntsP thrpt 40 55117.582 ± 650.154 ops/s
EncodingBenchmark.encodeIntsPico thrpt 40 31602.757 ± 351.578 ops/s
EncodingBenchmark.encodeIntsS thrpt 40 40509.667 ± 560.439 ops/s
ParsingBenchmark.parseFoosC thrpt 40 2869.779 ± 61.898 ops/s
ParsingBenchmark.parseFoosA thrpt 40 2615.299 ± 25.881 ops/s
ParsingBenchmark.parseFoosP thrpt 40 1970.493 ± 90.383 ops/s
ParsingBenchmark.parseFoosPico thrpt 40 3113.232 ± 29.081 ops/s
ParsingBenchmark.parseFoosS thrpt 40 3725.056 ± 68.794 ops/s
ParsingBenchmark.parseIntsC thrpt 40 13062.151 ± 209.713 ops/s
ParsingBenchmark.parseIntsA thrpt 40 11066.850 ± 159.308 ops/s
ParsingBenchmark.parseIntsP thrpt 40 18980.265 ± 91.351 ops/s
ParsingBenchmark.parseIntsPico thrpt 40 15184.314 ± 37.808 ops/s
ParsingBenchmark.parseIntsS thrpt 40 15495.935 ± 388.922 ops/s
PrintingBenchmark.printFoosC thrpt 40 4090.218 ± 38.804 ops/s
PrintingBenchmark.printFoosA thrpt 40 2863.570 ± 19.091 ops/s
PrintingBenchmark.printFoosP thrpt 40 9042.816 ± 49.199 ops/s
PrintingBenchmark.printFoosPico thrpt 40 4759.601 ± 20.467 ops/s
PrintingBenchmark.printFoosS thrpt 40 7297.047 ± 28.168 ops/s
PrintingBenchmark.printIntsC thrpt 40 24596.715 ± 66.366 ops/s
PrintingBenchmark.printIntsA thrpt 40 15611.121 ± 140.017 ops/s
PrintingBenchmark.printIntsP thrpt 40 66283.874 ± 731.534 ops/s
PrintingBenchmark.printIntsPico thrpt 40 23703.796 ± 188.186 ops/s
PrintingBenchmark.printIntsS thrpt 40 53015.753 ± 462.472 ops/s
And allocation rates (lower is better):
Benchmark Mode Cnt Score Error Units
DecodingBenchmark.decodeFoosC:gc.alloc.rate.norm thrpt 20 1308424.455 ± 0.881 B/op
DecodingBenchmark.decodeFoosA:gc.alloc.rate.norm thrpt 20 3779097.640 ± 2.456 B/op
DecodingBenchmark.decodeFoosP:gc.alloc.rate.norm thrpt 20 2201336.820 ± 1.588 B/op
DecodingBenchmark.decodeFoosPico:gc.alloc.rate.norm thrpt 20 506696.832 ± 1.608 B/op
DecodingBenchmark.decodeFoosS:gc.alloc.rate.norm thrpt 20 273184.238 ± 0.458 B/op
DecodingBenchmark.decodeIntsC:gc.alloc.rate.norm thrpt 20 291360.090 ± 0.174 B/op
DecodingBenchmark.decodeIntsA:gc.alloc.rate.norm thrpt 20 655448.200 ± 0.387 B/op
DecodingBenchmark.decodeIntsP:gc.alloc.rate.norm thrpt 20 369144.097 ± 0.189 B/op
DecodingBenchmark.decodeIntsPico:gc.alloc.rate.norm thrpt 20 235400.144 ± 0.280 B/op
DecodingBenchmark.decodeIntsS:gc.alloc.rate.norm thrpt 20 38136.021 ± 0.041 B/op
EncodingBenchmark.encodeFoosC:gc.alloc.rate.norm thrpt 20 395272.225 ± 0.433 B/op
EncodingBenchmark.encodeFoosA:gc.alloc.rate.norm thrpt 20 521136.306 ± 0.595 B/op
EncodingBenchmark.encodeFoosP:gc.alloc.rate.norm thrpt 20 1367800.719 ± 7.263 B/op
EncodingBenchmark.encodeFoosPico:gc.alloc.rate.norm thrpt 20 281992.346 ± 0.674 B/op
EncodingBenchmark.encodeFoosS:gc.alloc.rate.norm thrpt 20 377856.318 ± 0.615 B/op
EncodingBenchmark.encodeIntsC:gc.alloc.rate.norm thrpt 20 64160.016 ± 7.129 B/op
EncodingBenchmark.encodeIntsA:gc.alloc.rate.norm thrpt 20 80152.023 ± 0.044 B/op
EncodingBenchmark.encodeIntsP:gc.alloc.rate.norm thrpt 20 71352.030 ± 0.058 B/op
EncodingBenchmark.encodeIntsPico:gc.alloc.rate.norm thrpt 20 58992.057 ± 0.115 B/op
EncodingBenchmark.encodeIntsS:gc.alloc.rate.norm thrpt 20 76176.042 ± 0.081 B/op
ParsingBenchmark.parseFoosC:gc.alloc.rate.norm thrpt 20 765800.586 ± 1.133 B/op
ParsingBenchmark.parseFoosA:gc.alloc.rate.norm thrpt 20 1488760.635 ± 1.228 B/op
ParsingBenchmark.parseFoosP:gc.alloc.rate.norm thrpt 20 987720.805 ± 1.551 B/op
ParsingBenchmark.parseFoosPico:gc.alloc.rate.norm thrpt 20 639464.525 ± 1.014 B/op
ParsingBenchmark.parseFoosS:gc.alloc.rate.norm thrpt 20 252256.440 ± 0.838 B/op
ParsingBenchmark.parseIntsC:gc.alloc.rate.norm thrpt 20 121272.129 ± 0.250 B/op
ParsingBenchmark.parseIntsA:gc.alloc.rate.norm thrpt 20 310280.151 ± 0.289 B/op
ParsingBenchmark.parseIntsP:gc.alloc.rate.norm thrpt 20 216448.089 ± 0.171 B/op
ParsingBenchmark.parseIntsPico:gc.alloc.rate.norm thrpt 20 141808.118 ± 0.239 B/op
ParsingBenchmark.parseIntsS:gc.alloc.rate.norm thrpt 20 109000.117 ± 0.229 B/op
PrintingBenchmark.printFoosC:gc.alloc.rate.norm thrpt 20 425240.419 ± 0.810 B/op
PrintingBenchmark.printFoosA:gc.alloc.rate.norm thrpt 20 621288.585 ± 1069.068 B/op
PrintingBenchmark.printFoosP:gc.alloc.rate.norm thrpt 20 351360.184 ± 0.356 B/op
PrintingBenchmark.printFoosPico:gc.alloc.rate.norm thrpt 20 431268.348 ± 1058.404 B/op
PrintingBenchmark.printFoosS:gc.alloc.rate.norm thrpt 20 372992.228 ± 0.442 B/op
PrintingBenchmark.printIntsC:gc.alloc.rate.norm thrpt 20 74464.067 ± 7.127 B/op
PrintingBenchmark.printIntsA:gc.alloc.rate.norm thrpt 20 239712.107 ± 0.206 B/op
PrintingBenchmark.printIntsP:gc.alloc.rate.norm thrpt 20 24144.025 ± 0.048 B/op
PrintingBenchmark.printIntsPico:gc.alloc.rate.norm thrpt 20 95472.072 ± 0.140 B/op
PrintingBenchmark.printIntsS:gc.alloc.rate.norm thrpt 20 24048.032 ± 0.062 B/op
The Foos
benchmarks work with a map containing case class values, and the Ints
ones are an array
of integers. C
suffixes indicate circe's throughput, A
is for Argonaut, P
is for
[play-json][play-json], Pico
is for [picopickle][picopickle], and S
is for
[spray-json][spray-json]. Note that spray-json's approach to failure handling is different from the
approaches of the other libraries listed here (it simply throws exceptions), and this difference
should be taken into account when comparing its results with the others.
This section needs a lot of expanding.
circe uses Encoder
and Decoder
type classes for encoding and decoding. An Encoder[A]
instance
provides a function that will convert any A
to a JSON
, and a Decoder[A]
takes a Json
value
to either an exception or an A
. circe provides implicit instances of these type classes for many
types from the Scala standard library, including Int
, String
, and [others][encoder]. It also
provides instances for List[A]
, Option[A]
, and other generic types, but only if A
has an
Encoder
instance.
Suppose we have the following JSON document:
import io.circe._, io.circe.generic.auto._, io.circe.jawn._, io.circe.syntax._
import cats.data.Xor
val json: String = """
{
"id": "c730433b-082c-4984-9d66-855c243266f0",
"name": "Foo",
"counts": [1, 2, 3],
"values": {
"bar": true,
"baz": 100.001,
"qux": ["a", "b"]
}
}
"""
val doc: Json = parse(json).getOrElse(Json.empty)
In order to transform this document we need to create an HCursor
with the focus at the document's
root:
val cursor: HCursor = doc.hcursor
We can then use [various operations][generic-cursor] to move the focus of the cursor around the document and to "modify" the current focus:
val reversedNameCursor: ACursor =
cursor.downField("name").withFocus(_.mapString(_.reverse))
We can then return to the root of the document and return its value with top
:
val reversedName: Option[Json] = reversedNameCursor.top
The result will contain the original document with the "name"
field reversed.
circe is a fork of Argonaut, and if you find it at all useful, you should thank [Mark Hibberd][markhibberd], [Tony Morris][tonymorris], [Kenji Yoshida][xuwei-k], and the rest of the [Argonaut contributors][argonaut-contributors].
circe is currently maintained by [Travis Brown][travisbrown], [Alexandre Archambault][archambault], and [Vladimir Kostyukov][vkostyukov]. After the 0.4.0 release, all pull requests will require two sign-offs by a maintainer to be merged.
The circe project supports the [Typelevel][typelevel] [code of conduct][code-of-conduct] and wants all of its channels (Gitter, GitHub, etc.) to be welcoming environments for everyone.
Please see the contributors' guide for details on how to submit a pull request.
-
Please note that generic derivation will not work on Scala 2.10 unless you've added the [Macro Paradise][paradise] plugin to your build. See the quick start section above for details.
-
Generic derivation may not work as expected when the type definitions that you're trying to derive instances for are at the same level as the attempted derivation. For example:
scala> import io.circe.Decoder, io.circe.generic.auto._ import io.circe.Decoder import io.circe.generic.auto._
scala> sealed trait A; case object B extends A; object X { val d = Decoder[A] } defined trait A defined object B defined object X
scala> object X { sealed trait A; case object B extends A; val d = Decoder[A] } :19: error: could not find implicit value for parameter d: io.circe.Decoder[X.A] object X { sealed trait A; case object B extends A; val d = Decoder[A] } ^
This is unfortunately a limitation of the macro API that Shapeless uses to derive the generic
representation of the sealed trait. You can manually define these instances, or you can arrange
the sealed trait definition so that it is not in the same immediate scope as the attempted
derivation (which is typically what you want, anyway).
3. For large or deeply-nested case classes and sealed trait hierarchies, the generic derivation
provided by the `generic` subproject may stack overflow during compilation, which will result in
the derived encoders or decoders simply not being found. Increasing the stack size available to
the compiler (e.g. with `sbt -J-Xss64m` if you're using SBT) will help in many cases, but we have
at least [one report][very-large-adt] of a case where it doesn't.
4. More generally, the generic derivation provided by the `generic` subproject works for a wide
range of test cases, and is likely to _just work_ for you, but it relies on macros (provided by
Shapeless) that rely on compiler functionality that is not always perfectly robust
("[SI-7046][si-7046] is like [playing roulette][si-7046-roulette]"), and if you're running into
problems, it's likely that they're not your fault. Please file an issue here or ask a question on
the [Gitter channel][gitter], and we'll do our best to figure out whether the problem is
something we can fix.
## License
circe is licensed under the **[Apache License, Version 2.0][apache]** (the
"License"); you may not use this software except in compliance with the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[apache]: http://www.apache.org/licenses/LICENSE-2.0
[api]: https://travisbrown.github.io/circe/api/#io.circe.package
[archambault]: https://twitter.com/alxarchambault
[argonaut]: http://argonaut.io/
[argonaut-contributors]: https://github.com/argonaut-io/argonaut/graphs/contributors
[argonaut-shapeless]: https://github.com/alexarchambault/argonaut-shapeless
[benchmarks]: https://github.com/travisbrown/circe/blob/master/benchmark/src/main/scala/io/circe/benchmark/Benchmark.scala
[cats]: https://github.com/typelevel/cats
[circe-generic]: https://travisbrown.github.io/circe/api/#io.circe.generic.auto$
[circe-jackson]: https://travisbrown.github.io/circe/api/#io.circe.jackson.package
[circe-jawn]: https://travisbrown.github.io/circe/api/#io.circe.jawn.package
[circe-parser]: https://travisbrown.github.io/circe/api/#io.circe.parser.package
[code-of-conduct]: http://typelevel.org/conduct.html
[discipline]: https://github.com/typelevel/discipline
[encoder]: https://travisbrown.github.io/circe/api/#io.circe.Encoder$
[export-hook]: https://github.com/milessabin/export-hook
[finch]: https://github.com/finagle/finch
[generic-cursor]: https://travisbrown.github.io/circe/api/#io.circe.GenericCursor
[gitter]: https://gitter.im/travisbrown/circe
[incompletes]: https://meta.plasm.us/posts/2015/06/21/deriving-incomplete-type-class-instances/
[jackson]: https://github.com/FasterXML/jackson
[jawn]: https://github.com/non/jawn
[markhibberd]: https://github.com/markhibberd
[maven-central]: http://search.maven.org/
[monocle]: https://github.com/julien-truffaut/Monocle
[optics-pr]: https://github.com/travisbrown/circe/pull/78
[paradise]: http://docs.scala-lang.org/overviews/macros/paradise.html
[picopickle]: https://github.com/netvl/picopickle
[play-json]: https://www.playframework.com/documentation/2.4.x/ScalaJson
[scala-js]: http://www.scala-js.org/
[scalaz]: https://github.com/scalaz/scalaz
[shapeless]: https://github.com/milessabin/shapeless
[si-7046]: https://issues.scala-lang.org/browse/SI-7046
[si-7046-roulette]: https://twitter.com/li_haoyi/status/637281580847878145
[spool]: https://twitter.github.io/util/docs/index.html#com.twitter.concurrent.Spool
[spray-json]: https://github.com/spray/spray-json
[tonymorris]: https://github.com/tonymorris
[travisbrown]: https://twitter.com/travisbrown
[typelevel]: http://typelevel.org/
[util]: https://github.com/twitter/util
[very-large-adt]: http://stackoverflow.com/questions/33318802/scala-parse-json-of-more-than-22-elements-into-case-class/33319168?noredirect=1#comment55069438_33319168
[vkostyukov]: https://twitter.com/vkostyukov
[xuwei-k]: https://github.com/xuwei-k