JSON Values and the Joy of Scala
JSON Values and the Joy of Scala
In this article, we introduce json-values, a Scala library that makes use of persistent data structures to allow users to better handle JSON.
Join the DZone community and get the full member experience.Join For Free
One of the most important aspects of functional programming is immutable data structures, better known as values. Updating these structures using the copy-on-write approach is very inefficient, and this is the reason why persistent data structures were created.
On the other hand, JSON is a lightweight, text-based, language-independent data interchange format. It's become so popular due to its simplicity. There are a lot of libraries out there to work with JSON in the JVM ecosystem; however, none of them use persistent data structures.
In most cases, those libraries parse a string or array of bytes into an object. The thing is, why do that? JSON is a great structure. It's simple, easy to aggregate, ease to create, easy to reason about, so why create yet another abstraction over JSON?
Moreover, there are many architectures that work with JSON end-to-end. Going from JSON to objects or strings back and forth is not very efficient, especially when copy-on-write is the only option to avoid mutation. All these points are way better elaborated in the talk the value of values, a masterpiece from Rich Hickey. Json-values, the library I'm introducing here, was named after that talk.
json-values is a functional JSON library in Scala that uses persistent data structures. In this first article, we are going to focus on two more important aspects of software development where json-values can make a difference: data validation and testing.
To test json-values, I did property-based-testing with ScalaCheck. It's a great library where creating generators and composing them is very simple. To generate JSON values, I created json-values-generators, a series of generators to work with ScalaCheck. I argue that it's the most declarative and beautiful Json generator in the world!
You may also like: Using JSON With Play and Scala.
Validation is extremely important in software. Corrupt data can propagate throughout your system and cause a nightmare. Errors that blow up in your face are way better! If you think about it, the definition, validation, and generation of a JSON value could be implemented using the same data structure; after all, the three of them are just bindings with different elements: values, value generators, or value specs. Let's check out an example:
As you can see, creating specs and generators is as simple as creating raw JSON. Writing specs and generators for our tests is child's play. It has enormous advantages for development, such as:
- Increase productivity.
- More readable code. The more readable code is, the easier it is to maintain and reason about that code.
Let's focus for a while on what I call json-spec. I named it after spec, a Clojure library. It's really powerful and composable. Any spec can be defined in terms of predicates. Let's put a more elaborated example:
A spec can be used to validate existing JSON:
Returning a lazy list decouples the validate function from its consumers. Consumers can take only an error if they are just interested in whether the JSON is valid or not, or they can take them all to print out a report.
In both cases, they are invoking the same function. A json-spec can be also used to parse a string or an array of bytes into JSON. Instead of parsing the whole string and then validating it, we can validate the JSON while parsing it and stop the parsing as soon as an error happens. After all, failing fast is important as well!
Let's move on and introduce some of the most important functions in the library:
One important thing about the
map functions listed above is that they are functors, so the structure of the JSON doesn't change.
Some optics to work with json-values have been defined in a different library. I didn't want to be opinionated about anything. There are some libraries to create optics out there and the users of json-values are free to use their favorite ones. I use monocle. For example, Prisms are really useful to pass in functions to filter and map:
This is just a quick introduction to json-values. Go to the project page for further details. At this moment, I'm migrating the library to Scala 3 and adding some more documentation.
- json-values implements an immutable Json with persistent data structures.
- Specs and generators are composable data structures that are defined just as Jsons.
- json-values is simple, and as Rich Hickey said simplicity matters.
- Scala is a great language that makes it possible for developers to write expressive and beautiful code.
Opinions expressed by DZone contributors are their own.