agentsclimarketplace

Scala circe json

Skill cxcscmu/SkillLearnBench/skills/b1-one-shot-claude-haiku-4-5/python-scala-translation/scala-circe-json

Working with Circe JSON library in Scala for type-safe JSON processingFrom its SKILL.md

Install
npx -y skills add cxcscmu/SkillLearnBench --skill scala-circe-json

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

7.5 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it

Scala Circe JSON Processing

Overview

Circe is the standard type-safe JSON library in Scala. It provides:

  • Immutable Json type
  • Type-safe codecs (Encoder/Decoder)
  • Cursor-based navigation (like XPath for JSON)
  • Pretty printing
  • Seamless case class integration

Installation

Add to build.sbt:

libraryDependencies ++= Seq(
  "io.circe" %% "circe-core" % "0.14.5",
  "io.circe" %% "circe-parser" % "0.14.5",
  "io.circe" %% "circe-generic" % "0.14.5"
)

Basic Usage

Parsing JSON

import io.circe.parser._
import io.circe._

// Parse from string
val jsonString = """{"name": "Alice", "age": 30}"""
val result: Either[io.circe.ParsingFailure, Json] = parse(jsonString)

// Safe extraction
result match {
  case Right(json) => println(s"Parsed: $json")
  case Left(error) => println(s"Parse error: $error")
}

// Or with Try
val json: scala.util.Try[Json] = parse(jsonString).toTry

Creating JSON

import io.circe.syntax._

// From primitives
val str = Json.fromString("hello")
val num = Json.fromInt(42)
val bool = Json.fromBoolean(true)
val null_val = Json.Null

// Object (using syntax extension)
val obj = Json.obj(
  "name" -> Json.fromString("Alice"),
  "age" -> Json.fromInt(30)
)

// Array
val arr = Json.arr(
  Json.fromInt(1),
  Json.fromInt(2),
  Json.fromInt(3)
)

// Using tuple extension (cleaner)
val obj = Json.obj(
  "name" -> "Alice".asJson,
  "age" -> 30.asJson,
  "scores" -> List(90, 85, 88).asJson
)

Working with Json Type

val json = Json.obj(
  "name" -> "Alice".asJson,
  "age" -> 30.asJson
)

// Type checks
json.isObject    // true
json.isArray     // false
json.isNull      // false

// Conversions
json.asObject    // Option[JsonObject]
json.asArray     // Option[Vector[Json]]
json.asString    // Option[String]
json.asNumber    // Option[JsonNumber]
json.asBoolean   // Option[Boolean]

// Unsafe conversion (throws)
json.asObject.get

Navigation with Cursors

Cursors provide safe, composable navigation (like XPath):

import io.circe.{Json, HCursor}

val json = parse("""
{
  "person": {
    "name": "Alice",
    "addresses": [
      {"city": "NYC"},
      {"city": "Boston"}
    ]
  }
}
""").getOrElse(Json.Null)

// Creating cursor
val cursor: HCursor = json.hcursor

// Navigation
val name: Either[DecodingFailure, String] =
  cursor
    .downField("person")
    .downField("name")
    .as[String]

// Navigate arrays
val firstCity: Either[DecodingFailure, String] =
  cursor
    .downField("person")
    .downField("addresses")
    .downArray
    .downField("city")
    .as[String]

// Optional navigation (returns Json)
cursor.downField("person").downField("name").focus  // Option[Json]

Cursor Methods

val cursor = json.hcursor

// Navigation
cursor.downField("fieldName")   // Navigate to field
cursor.downArray                // Navigate to first array element
cursor.up                       // Go to parent
cursor.left                     // Move to previous array element
cursor.right                    // Move to next array element

// Extraction
cursor.focus                    // Option[Json] - current value
cursor.as[String]               // Either[DecodingFailure, String]

// Checking
cursor.succeeded               // Boolean - did navigation succeed?

Type-Safe Encoding/Decoding

For Case Classes

import io.circe._, io.circe.generic.semiauto._

case class Person(name: String, age: Int)

// Automatic codec generation
implicit val encoder: Encoder[Person] = deriveEncoder[Person]
implicit val decoder: Decoder[Person] = deriveDecoder[Person]

// Usage
val person = Person("Alice", 30)
val json = person.asJson
val decoded = json.as[Person]  // Either[DecodingFailure, Person]

Custom Codecs

implicit val customEncoder: Encoder[Person] = new Encoder[Person] {
  def apply(p: Person): Json = Json.obj(
    "fullName" -> Json.fromString(p.name),
    "years" -> Json.fromInt(p.age)
  )
}

implicit val customDecoder: Decoder[Person] = new Decoder[Person] {
  def apply(c: HCursor): Decoder.Result[Person] = for {
    name <- c.downField("fullName").as[String]
    age <- c.downField("years").as[Int]
  } yield Person(name, age)
}

Recursive JSON Structures

For recursive types like JSON itself:

// Representing JSON recursively
sealed trait JsonValue
case class JString(value: String) extends JsonValue
case class JNumber(value: Double) extends JsonValue
case class JBoolean(value: Boolean) extends JsonValue
case object JNull extends JsonValue
case class JArray(value: Vector[JsonValue]) extends JsonValue
case class JObject(value: Map[String, JsonValue]) extends JsonValue

// Or use Circe's Json type directly (which IS recursive)
val json: Json = Json.obj(
  "data" -> Json.arr(
    Json.obj("id" -> 1.asJson),
    Json.obj("id" -> 2.asJson)
  )
)

Pretty Printing

import io.circe.Printer

val json = Json.obj("name" -> "Alice".asJson, "age" -> 30.asJson)

// Compact
json.toString()
// {"name":"Alice","age":30}

// Pretty printed
val printer = Printer.spaces2  // 2-space indent
println(printer.print(json))
// {
//   "name" : "Alice",
//   "age" : 30
// }

// Custom printer
val customPrinter = Printer(
  dropNullValues = false,
  indent = "  "
)

JSON Path Extraction (for tokenize_path)

def getValueAtPath(json: Json, path: String): Option[Json] = {
  val parts = path.split("\\.")
  parts.foldLeft(Option(json)) { case (current, part) =>
    current.flatMap { j =>
      if (j.isObject) {
        j.hcursor.downField(part).focus
      } else if (j.isArray) {
        part.toIntOption.flatMap { idx =>
          j.asArray.flatMap(_.lift(idx))
        }
      } else {
        None
      }
    }
  }
}

// Usage
val json = parse("""{"person": {"name": "Alice", "scores": [90, 85]}}""").getOrElse(Json.Null)
val name = getValueAtPath(json, "person.name")      // Some("Alice")
val score = getValueAtPath(json, "person.scores.0") // Some(90)
val missing = getValueAtPath(json, "person.email")  // None

Common Patterns for Tokenizer

JSON Tokenization

import io.circe.Json
import io.circe.syntax._

class JsonTokenizer(pretty: Boolean = false) {
  def tokenize(json: Json): Token = {
    val jsonStr = if (pretty) {
      Printer.spaces2.print(json)
    } else {
      json.toString()
    }
    Token(jsonStr, TokenType.STRUCTURED, Map("json" -> true.asJson))
  }

  def tokenizePath(json: Json, path: String): Option[Token] = {
    getValueAtPath(json, path).map(tokenize)
  }

  private def getValueAtPath(json: Json, path: String): Option[Json] = {
    val parts = path.split("\\.")
    parts.foldLeft(Option(json)) { case (current, part) =>
      current.flatMap { j =>
        if (j.isObject) j.hcursor.downField(part).focus
        else if (j.isArray) {
          part.toIntOption.flatMap(idx => j.asArray.flatMap(_.lift(idx)))
        } else None
      }
    }
  }
}

Type Safety Benefits

Unlike Python's duck typing with JSON:

# Python - runtime errors possible
data = {"name": "Alice"}
age = data["age"]  # KeyError!
// Scala - compile-time safety
val json = parse("""{"name": "Alice"}""").getOrElse(Json.Null)
val age: Option[Int] = json.hcursor.downField("age").as[Int].toOption
// Type mismatch caught at compile time

Circe vs Manual JSON Handling

// Manual approach (error-prone)
val jsonStr = s"""{"value": "$value", "type": "$tokenType"}"""

// Circe approach (safe, composable)
val token = Token(value, tokenType)
val json = token.asJson  // With derived encoders

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.