In this tutorial, you will learn how to parse JSon literals into Circe Json and then use a Decoder to decoder the Circe Json into instances of the case classes.
We would take a number of examples. I also recommend you watch the
We would cover the following:
- Example 1 – Personal Data Json Object
- Create the Case Classes
- Build Your Decoders
- Decode Using Your Decoders
1. Example 1 – Personal Data Json
Let’s assume we have a the json literal below which represents personal data
{
"firstName": "John",
"lastName": "Smith",
"address": {
"street": "45 Downing Street",
"city": "New Mexico",
"state": "OK",
"postCode": 50034
},
"phoneNumbers": ["02982932", "034832939", "012894837"]
}
From the Json above, you can see that there are a number of types which includes:
- String – the firstName and LastName
- Address – a class with 3 fields
- phoneNumbers – a list of Strings
So we would have to create 2 case classes:
- PersonaData – a class that can instantiate the objects that encodable into the Json above
- Address – a class that can instantiate objects encodable into the address block of the Json
2. Create the Case Classes
Our case classes are given below:
The Address case class
case class Address( street: String, city: String, state: String, postCode: String )
The Personal Data case class
case class PersonalData( firstName: String, lastName: String, address: Address, phoneNumber: List[String] )
3. Build Your Decoders
Now that we have the case classes, let’s proceed to build the decoders. We will building a monadic decoder using for comprehension.
Let’s begin by building the addressDecoder. This is given below:
// Decoder for Address implicit val addressDecoder:Decoder[Address] = addressCursor => for { street <- addressCursor.get[String]("street") city <- addressCursor.get[String]("city") state <- addressCursor.get[String]("state") postCode <- addressCursor.get[Int]("postCode") } yield Address(street, city, state, postCode)
Next, we build the personal data decoder the same way using for comprehension as well.
// Decoder for PersonalData implicit val personalDataDecoder:Decoder[PersonalData] = personalDataCursor => for { firstName <- personalDataCursor.get[String]("firstName") lastName <- personalDataCursor.get[String]("lastName") address <- personalDataCursor.get[Address]("address") phoneNumbers <- personalDataCursor.get[List[String]]("phoneNumbers") } yield PersonalData(firstName, lastName, address, phoneNumbers)
4. Decode Using our Decoders
Now we can use our decoders. So we can take two steps:
- Step 1 – Parse the Json literal into io.circe.json
- Step 2 – Decode the Json into an instance of a class
// Parse the json literal val personalDataLiteralParsed = parse(personalDataLiteral)
We have to extract json from the parsed literal because the result of parsing is an Either(parsingFailure, Json). Therefore we simply take the Json value from the right (assuming there’s no failure, we don’t try to match the Left). The code is shown below:
// Extract the json from the parsed literal val personalDataJson:Json = personalDataLiteralParsed match { case Right(value) => value case Left(value) => value.message.asJson }
Finally, you can the decode the parsed personal data object using one the two methods given below:
//Using the personalDataDecoder from the implicit scope val personalDataDecoded = personalDataDecoder(personalDataJson.hcursor) //Using the as method val personalDataDecoded2 = personalDataJson.as[PersonalData]
Hint: You can go directly from the original json literal to the PersonalData instance using. In this way you circe, performs both the parsing and decoding in one go.
val personalDataDecoded3 = decode[PersonalData](personalDataLiteral)