I have a JSON that can change through time and using case Class might be unconvenient because I need to change the structure of it everytime the JSON change.
for example, if I have a JSON like this:
val json= """{
"accounts": [
{ "emailAccount": {
"accountName": "YMail",
"username": "USERNAME",
"password": "PASSWORD",
"url": "imap.yahoo.com",
"minutesBetweenChecks": 1,
"usersOfInterest": ["barney", "betty", "wilma"]
}},
{ "emailAccount": {
"accountName": "Gmail",
"username": "USER",
"password": "PASS",
"url": "imap.gmail.com",
"minutesBetweenChecks": 1,
"usersOfInterest": ["pebbles", "bam-bam"]
}}
]
}"""
can I access to it with something like:
val parsedJSON = parse(json)
parsedJSON.accounts(0).emailAccount.accountName
circe's optics module supports almost exactly the syntax you're asking for:
import io.circe.optics.JsonPath.root
val accountName = root.accounts.at(0).emailAccount.accountName.as[String]
And then if you've got this JSON value (I'm using circe's JSON literal support, but you could also parse a string with io.circe.jawn.parse
(parse function) to get the Json
value you're working with):
import io.circe.Json, io.circe.literal._
val json: Json = json"""{
"accounts": [
{ "emailAccount": {
"accountName": "YMail",
"username": "USERNAME",
"password": "PASSWORD",
"url": "imap.yahoo.com",
"minutesBetweenChecks": 1,
"usersOfInterest": ["barney", "betty", "wilma"]
}},
{ "emailAccount": {
"accountName": "Gmail",
"username": "USER",
"password": "PASS",
"url": "imap.gmail.com",
"minutesBetweenChecks": 1,
"usersOfInterest": ["pebbles", "bam-bam"]
}}
]
}"""
You can do try to access the account name like this:
scala> accountName.getOption(json)
res0: Option[String] = Some(YMail)
Because circe-optics is built on Monocle, you get some other nice functionality, like immutable updates:
scala> accountName.modify(_.toLowerCase)(json)
res2: io.circe.Json =
{
"accounts" : [
{
"emailAccount" : {
"accountName" : "ymail",
...
And so on.
Update: circe is designed to be modular, so that you only "pay for" the pieces you need. The examples above expect something like the following setup for SBT:
scalaVersion := "2.11.8"
val circeVersion = "0.4.1"
libraryDependencies ++= Seq(
"io.circe" %% "circe-core" % circeVersion,
"io.circe" %% "circe-jawn" % circeVersion,
"io.circe" %% "circe-literal" % circeVersion,
"io.circe" %% "circe-optics" % circeVersion
)
…or for Maven:
<dependency>
<groupId>io.circe</groupId>
<artifactId>circe-core_2.11</artifactId>
<version>0.4.1</version>
</dependency>
<dependency>
<groupId>io.circe</groupId>
<artifactId>circe-jawn_2.11</artifactId>
<version>0.4.1</version>
</dependency>
<dependency>
<groupId>io.circe</groupId>
<artifactId>circe-literal_2.11</artifactId>
<version>0.4.1</version>
</dependency>
<dependency>
<groupId>io.circe</groupId>
<artifactId>circe-optics_2.11</artifactId>
<version>0.4.1</version>
</dependency>