October 1, 2025

Scala – Parsing and Decoding Json with Circe

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:

  1. Example 1 – Personal Data Json Object
  2. Create the Case Classes
  3. Build Your Decoders
  4. 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)

 

Video Tutorial Here

Leave a Reply