Klaxon Kotlin JSON Library

repository·master·Indexed 23 days ago

https://github.com/cbeust/klaxon

A Kotlin library for parsing JSON featuring four distinct APIs: an Object binding API for mapping JSON to Kotlin classes, a Streaming API for memory-efficient processing, a Low-level API for manual inspection via JsonObject and JsonArray, and a JSON Path query API. It supports custom type converters, polymorphic handling via @TypeFor, and offers both a default Kotlin parser and a high-performance Jackson-based parser.

Tokens
7.1K
Snippets
14
Records
38
Agent score
83%

What's inside Klaxon

  1. Overview of Klaxon APIs

    master

    Klaxon provides four distinct APIs depending on whether you need to stream data, query specific parts of a document, or manipulate JSON directly. Choose the API that best fits your performance and functional requirements:

    APIStreamingQueryManipulation
    Object binding APINoNoKotlin objects
    Streaming APIYesNoKotlin objects and JsonObject/JsonArray
    Low level APINoYesKotlin objects
    JSON Path query APIYesYesJsonObject/JsonArray
  2. Choose between the default and Jackson parsers

    master

    Klaxon provides two official parser implementations depending on your performance needs:

    1. Default Parser: Written in Kotlin. Access it using Parser.default(). It is the standard implementation.
    2. Jackson Parser: Uses the FasterXML Jackson mapper. It is optimized for large JSON payloads and can be up to 2x faster than the default parser.

    To use the Jackson parser, you must add the dependency com.beust:klaxon-jackson:[version] to your project and call Parser.jackson().

  3. Handle polymorphism with @TypeFor

    master

    Klaxon supports polymorphism via the @TypeFor annotation, which can be used in two ways:

    1. Polymorphic Classes

    Use this when a property within the class determines the class type itself. The annotation requires a field (the name of the discriminant property) and an adapter (a class implementing TypeAdapter).

    @TypeFor(field = "type", adapter = ShapeTypeAdapter::class)
    open class Shape(val type: String)
    data class Rectangle(val width: Int, val height: Int): Shape("rectangle")

    2. Polymorphic Fields

    Use this when a field in a parent object determines the type of another field. The annotation is placed on the discriminant field and points to the field that is polymorphic.

    class Data (
        @TypeFor(field = "shape", adapter = ShapeTypeAdapter::class)
        val type: Integer,
        val shape: Shape
    )
  4. Use JsonValue to inspect JSON data in converters

    master
    The JsonValue class is passed to the fromJson method of a Converter. It acts as a container that holds exactly one of: a number, a string, a character, a JsonObject, or a JsonArray. Use its helper methods (like .objInt(), .string, etc.) to extract the expected type. If the expected type is missing, you should throw a KlaxonException.
  5. Parse JSON into Kotlin objects using the Object binding API

    master

    Klaxon's high-level API allows you to map JSON documents directly to Kotlin classes. It supports regular classes, data classes, mutable and immutable classes, and classes with default parameters.

    To parse JSON, define your target class and use the parse<T>(jsonString) method on a Klaxon instance.

    class Person(val name: String, val age: Int)
    
    val result = Klaxon()
        .parse<Person>("""
        {
          "name": "John Smith"
        }
        """)
    
    assert(result?.name == "John Smith")
    assert(result.age == 23)
  6. Apply type conversion to specific fields using annotations

    master

    If you need a specific converter for a single field (e.g., a specific date format) rather than globally, create a custom annotation targeting AnnotationTarget.FIELD.

    1. Define your annotation: @Target(AnnotationTarget.FIELD) annotation class MyAnnotation.
    2. Annotate the field in your class (ensure the constructor uses @JvmOverloads).
    3. Register the association using .fieldConverter(MyAnnotation::class, myConverter) on the Klaxon instance.
    @Target(AnnotationTarget.FIELD)
    annotation class KlaxonDate
    
    class WithDate @JvmOverloads constructor(
        @KlaxonDate
        val date: LocalDateTime
    )
    
    val dateConverter = object: Converter {
        override fun canConvert(cls: Class<*>) = cls == LocalDateTime::class.java
        override fun fromJson(jv: JsonValue) = 
            if (jv.string != null) LocalDateTime.parse(jv.string, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
            else throw KlaxonException("Couldn't parse date")
        override fun toJson(o: Any) = "{\"date\" : $o}"
    }
    
    val result = Klaxon()
        .fieldConverter(KlaxonDate::class, dateConverter)
        .parse<WithDate>("{\"theDate\": \"2017-05-10 16:30\"}")
  7. Use the @Json annotation to map JSON fields

    master

    The @Json annotation is used to customize how class properties are mapped to JSON fields. You can use it to specify a custom name for the field in the JSON representation or to mark a property as ignored during serialization/deserialization.

    Key capabilities:

    • Renaming: Provide a specific name for the JSON key using the name parameter.
    • Ignoring: Use the ignored parameter to prevent a property from being processed.
    • Ordering: The index parameter (implied by the internal findProperties logic) can be used to influence the order of properties during processing.
  8. How the DefaultConverter works

    master

    The DefaultConverter is the fallback mechanism in Klaxon for JSON serialization and deserialization.

    Deserialization (fromJson): It attempts to convert a JsonValue into a Kotlin/Java object by:

    1. Checking if the value is a primitive (Boolean, String, Int, etc.).
    2. Handling numeric widening (e.g., converting an Int to a Long or BigDecimal if the target property type requires it).
    3. Recursively processing Collection and JsonObject types.
    4. Using reflection to map JSON object keys to class properties.

    Serialization (toJson): It converts a Kotlin/Java object into a JSON string by:

    1. Handling primitives and Enums directly.
    2. Iterating through Collection, Array, or Map types.
    3. For custom objects, it uses reflection to find non-ignored properties (respecting propertyStrategies) and retrieves their values. It respects both local @Json annotations and global klaxon.instanceSettings.serializeNull settings to determine if null values should be included in the output.
  9. How Klaxon resolves type converters

    master

    Klaxon uses a hierarchical approach to find the best Converter for a given type or property via findConverterFromClass. The resolution order is:

    1. Field-level Annotation: If the property has a marker annotation registered via fieldConverter, that converter is used.
    2. Class-level Converter: Klaxon searches the registered converters list for the first converter that canConvert the property's type.
    3. Property Type Converter: If the first step fails, it attempts to find a converter for the property's return type.
    4. Default Converter: If no specific converter is found, it falls back to the DEFAULT_CONVERTER (which handles standard object mapping).
  10. How EnumConverter handles JSON to Enum mapping

    master

    The EnumConverter allows for seamless conversion between JSON strings and Kotlin/Java Enums.

    Serialization (to JSON): When converting an enum to JSON, the converter looks for the @Json annotation on the enum field. If the annotation is present, it uses the name property defined in the annotation. If not, it defaults to the standard enum constant name.

    Deserialization (from JSON): When reading from JSON, the converter matches the JSON string against either the enum constant's name or the name specified in a @Json annotation on the field.

    Requirements:

    • The target class must be an Enum.
    • For custom names, use the @Json(name = "...") annotation on the enum constant.