Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Json Converter

Creating a JSON converter using type classes in Scala allows for flexible, reusable serialization logic that can be applied to various types without requiring modifications to those types. This approach is particularly useful when working with third-party classes or when you want to keep serialization logic decoupled from domain logic. Below is a simplified example demonstrating how to implement a JSON converter using type classes in Scala 3, utilizing the given and using syntax for clarity and explicitness.

Step 1: Define the JSON Type Class

First, define a trait that represents the ability to convert a value of type T to JSON.

trait JsonConverter[T] {
  def toJson(value: T): String
}

Step 2: Create Instances of the Type Class

Next, provide given instances of the JsonConverter for the types you want to support. Let's start with a few basic types like String and Int, and then create a converter for a custom class.

given JsonConverter[String] with {
  def toJson(value: String): String = s""""$value""""
}

given JsonConverter[Int] with {
  def toJson(value: Int): String = value.toString
}

// A sample case class for demonstration
case class Person(name: String, age: Int)

// Creating a JsonConverter for the Person case class
given JsonConverter[Person] with {
  def toJson(person: Person): String =
    s"""{"name": "${person.name}", "age": ${person.age}}"""
}

Step 3: Implement a Generic toJSON Function

Now, define a generic function that uses the JsonConverter type class to convert any supported type to JSON. This function will use the using clause to specify that it requires a JsonConverter for the type T.

def toJson[T](value: T)(using converter: JsonConverter[T]): String = {
  converter.toJson(value)
}

Step 4: Using the JSON Converter

Finally, you can use the toJson function to serialize different types to JSON. The compiler will automatically use the appropriate given instance based on the type of the value passed to toJson.

val name = "John Doe"
val age = 30
val person = Person(name, age)

println(toJson(name))   // Outputs: "John Doe"
println(toJson(age))    // Outputs: 30
println(toJson(person)) // Outputs: {"name": "John Doe", "age": 30}

Extensibility

One of the strengths of this approach is its extensibility. You can easily add support for new types by defining new given instances of the JsonConverter type class. This does not require modifying existing code, adhering to the open/closed principle.