SkillAgentSearch skills...

Klaxon

A JSON parser for Kotlin

Install / Use

/learn @cbeust/Klaxon
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<img src="doc/klaxon.png" alt="Klaxon logo" height="101" width="220" />

Klaxon is a library to parse JSON in Kotlin

Install

dependencies {
    implementation 'com.beust:klaxon:5.5'
}

Developer setup

Follow these steps to get the project running locally for development:

  • Prerequisites:
    • JDK 8 or 11 installed and JAVA_HOME set.
    • Git
  • Clone the repo:
git clone https://github.com/cbeust/klaxon.git
cd klaxon
  • Build the project:

Unix/macOS:

./gradlew build

Windows (PowerShell/CMD):

.\gradlew.bat build
  • Run tests:
./gradlew test
  • Run a single test class:
./gradlew test --tests com.beust.klaxon.BindingTest
  • Import into IDE: Import the project as a Gradle project in IntelliJ IDEA (use the Gradle wrapper). Enable auto-import for Gradle changes.

  • Useful module/task shortcuts:

    • Assemble JARs: ./gradlew assemble
    • Run aggregate task: ./gradlew publishToMavenLocal
    • Generate signature files: ./gradlew signCustomPublication

Community

Join the #klaxon Slack channel.

Use

Klaxon has different API's depending on your needs:

These four API's cover various scenarios and you can decide which one to use based on whether you want to stream your document and whether you need to query it.

| | Streaming | Query | Manipulation | |---------------------|-----------|--------------|--------------| | Object binding API | No | No | Kotlin objects | | Streaming API | Yes | No | Kotlin objects and JsonObject/JsonArray | | Low level API | No | Yes | Kotlin objects | | JSON Path query API | Yes | Yes | JsonObject/JsonArray |

Object binding API

General usage

To use Klaxon's high level API, you define your objects inside a class. Klaxon supports all the classes you can define in Kotlin:

  • Regular and data classes.
  • Mutable and immutable classes.
  • Classes with default parameters.

For example:

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

Classes with default parameters are supported as well:

class Person (val name: String, var age: Int = 23)

Once you've specified your value class, you invoke the parse() function, parameterized with that class:

val result = Klaxon()
    .parse<Person>("""
    {
      "name": "John Smith",
    }
    """)

assert(result?.name == "John Smith")
assert(result.age == 23)

The @Json annotation

The @Json annotation allows you to customize how the mapping between your JSON documents and your Kotlin objects is performed. It supports the following attributes:

name

Use the name attribute when your Kotlin property has a different name than the field found in your JSON document:

data class Person(
    @Json(name = "the_name")
    val name: String
)
val result = Klaxon()
    .parse<Person>("""
    {
      "the_name": "John Smith", // note the field name
      "age": 23
    }
""")

assert(result.name == "John Smith")
assert(result.age == 23)

ignored

You can set this boolean attribute to true if you want certain properties of your value class not to be mapped during the JSON parsing process. This is useful if you defined additional properties in your value classes.

class Ignored(val name: String) {
   @Json(ignored = true)
   val actualName: String get() = ...
}

In this example, Klaxon will not try to find a field called actualName in your JSON document.

Note that you can achieve the same result by declaring these properties private:

class Ignored(val name: String) {
   private val actualName: String get() = ...
}

Additionally, if you want to declare a property private but still want that property to be visible to Klaxon, you can annotate it with @Json(ignored = false).

index

The index attribute allows you to specify where in the JSON string the key should appear. This allows you to specify that certain keys should appear before others:

class Data(
    @Json(index = 1) val id: String,
    @Json(index = 2) val name: String
)
println(Klaxon().toJsonString(Data("id", "foo")))

// displays { "id": "id", "name": "foo" }

whereas

class Data(
    @Json(index = 2) val id: String,
    @Json(index = 1) val name: String
)
println(Klaxon().toJsonString(Data("id", "foo")))

// displays { "name": "foo" , "id": "id" }

Properties that are not assigned an index will be displayed in a non deterministic order in the output JSON.

serializeNull

By default, all properties with the value null are serialized to JSON, for example:

class Data(
    val id: Int?
)
println(Klaxon().toJsonString(Data(null)))

// displays { "id": null }

If you instead want the properties with a null value to be absent in the JSON string, use @Json(serializeNull = false):

class Data(
    @Json(serializeNull = false)
    val id: Int?
)
println(Klaxon().toJsonString(Data(null)))

// displays {}

If serializeNull is false, the Kotlin default values for this property will be ignored during parsing. Instead, if the property is absent in the JSON, the value will default to null.

If you don't want to apply this option to every attribute, you can also set it as an instance-wide setting for Klaxon:

val settings = KlaxonSettings(serializeNull = false)

This saves you the hassle of setting these attributes onto every single field.

data class User(
    val username: String, val email: String, // mandatory
    val phone: String?, val fax: String?, val age: Int? // optional
)

Klaxon(settings)
  .toJsonString(User("user", "user@example.org", null, null, null))

// displays {}

You may still set settings with the @Json annotation onto specific fields. They will take precedence over global settings of the Klaxon instance.

Renaming fields

On top of using the @Json(name=...) annotation to rename fields, you can implement a field renamer yourself that will be applied to all the fields that Klaxon encounters, both to and from JSON. You achieve this result by passing an implementation of the FieldRenamer interface to your Klaxon object:

    val renamer = object: FieldRenamer {
        override fun toJson(fieldName: String) = FieldRenamer.camelToUnderscores(fieldName)
        override fun fromJson(fieldName: String) = FieldRenamer.underscoreToCamel(fieldName)
    }

    val klaxon = Klaxon().fieldRenamer(renamer)

Custom types

Klaxon will do its best to initialize the objects with what it found in the JSON document but you can take control of this mapping yourself by defining type converters.

The converter interface is as follows:

interface Converter {
    fun canConvert(cls: Class<*>) : Boolean
    fun toJson(value: Any): String
    fun fromJson(jv: JsonValue) : Any
}

You define a class that implements this interface and implement the logic that converts your class to and from JSON. For example, suppose you receive a JSON document with a field that can either be a 0 or a 1 and you want to convert that field into your own type that's initialized with a boolean:

class BooleanHolder(val flag: Boolean)

val myConverter = object: Converter {
    override fun canConvert(cls: Class<*>)
        = cls == BooleanHolder::class.java

    override fun toJson(value: Any): String
        = """{"flag" : "${if ((value as BooleanHolder).flag == true) 1 else 0}"""

    override fun fromJson(jv: JsonValue)
        = BooleanHolder(jv.objInt("flag") != 0)

}

Next, you declare your converter to your Klaxon object before parsing:

val result = Klaxon()
    .converter(myConverter)
    .parse<BooleanHolder>("""
        { "flag" : 1 }
    """)

assert(result.flag)

JsonValue

The Converter type passes you an instance of the JsonValue class. This class is a container of a Json value. It is guaranteed to contain one and exactly one of either a number, a string, a character, a JsonObject or a JsonArray. If one of these fields is set, the others are guaranteed to be null. Inspect that value in your converter to make sure that the value you are expecting is present, otherwise, you can cast a KlaxonException to report the invalid JSON that you just found.

Field conversion overriding

It's sometimes useful to be able to specify a type conversion for a specific field without that conversion applying to all types of your document (for example, your JSON document might contain various dates of different formats). You can use field conversion types for this kind of situation.

Such fields are specified by your own annotation, which you need to specify as targetting a FIELD:

@Target(AnnotationTarget.FIELD)
annotation class KlaxonDate

Next, annotate the field that requires this specific handling in the constructor of your class. Do note that such a constructor needs to be annotated with @JvmOverloads:

class WithDate @JvmOverloads constructor(
    @KlaxonDate
    val date: LocalDateTime
)

Define your type converter (which has the same type as the converters defined previously). In this case, we are converting a String from JSON into a LocalDateTime:

val dateConverter = object

Related Skills

View on GitHub
GitHub Stars1.9k
CategoryDevelopment
Updated1d ago
Forks123

Languages

Kotlin

Security Score

100/100

Audited on Apr 1, 2026

No findings