A JSON library written in pure Kotlin

Overview

fluid-json

Maven Central Tests Kotlin #fluid-libraries Slack Channel

A JSON library written in pure Kotlin.

Table of Contents

Installation

build.gradle.kts:

plugins {
	kotlin("kapt")
}

dependencies {
	kapt("io.fluidsonic.json:fluid-json-annotation-processor:1.2.1")
	implementation("io.fluidsonic.json:fluid-json-coding-jdk8:1.2.1")
}

If you cannot use Java 8, e.g. when supporting Android API 25 or below, replace fluid-json-coding-jdk8 with fluid-json-coding.

If you're using IntelliJ IDEA (not Android Studio) then you have to manually enable the following project setting in order to use annotation processing directly within the IDE (this is an open issue in IntelliJ IDEA):
Preferences > Build, Execution, Deployment > Build Tools > Gradle > Runner > Delegate IDE build/run actions to gradle

Basic Usage

fluid-json uses @Json-annotations for automatically generating codec classes at compile-time which are responsible for decoding and encoding from and to JSON.
You can also create these codecs on your own instead of relying on annotation processing.

import io.fluidsonic.json.*

@Json
data class Event(
	val attendees: Collection<Attendee>,
	val description: String,
	val end: Instant,
	val id: Int,
	val start: Instant,
	val title: String
)

@Json
data class Attendee(
	val emailAddress: String,
	val firstName: String,
	val lastName: String,
	val rsvp: RSVP?
)

enum class RSVP {
	notGoing,
	going
}

Next you create a parser and a serializer that make use of the generated codecs:

import io.fluidsonic.json.*

fun main() {
	val data = Event(
		attendees = listOf(
			Attendee(emailAddress = "[email protected]", firstName = "Marc", lastName = "Knaup", rsvp = RSVP.going),
			Attendee(emailAddress = "[email protected]", firstName = "John", lastName = "Doe", rsvp = null)
		),
		description = "Discussing the fluid-json library.",
		end = Instant.now() + Duration.ofHours(2),
		id = 1,
		start = Instant.now(),
		title = "fluid-json MeetUp"
	)

	val serializer = JsonCodingSerializer.builder()
		.encodingWith(EventJsonCodec, AttendeeJsonCodec)
		.build()

	val serialized = serializer.serializeValue(data)
	println("serialized: $serialized")

	val parser = JsonCodingParser.builder()
		.decodingWith(EventJsonCodec, AttendeeJsonCodec)
		.build()

	val parsed = parser.parseValueOfType<Event>(serialized)
	println("parsed: $parsed")
}

Prints this:

serialized: {"attendees":[{"emailAddress":"[email protected]","firstName":"Marc","lastName":"Knaup","rsvp":"going"},{"emailAddress":"[email protected]","firstName":"John","lastName":"Doe","rsvp":null}],"description":"Discussing the fluid-json library.","end":"2019-03-05T00:45:08.335Z","id":1,"start":"2019-03-04T22:45:08.339Z","title":"fluid-json MeetUp"}

parsed: Event(attendees=[Attendee([email protected], firstName=Marc, lastName=Knaup, rsvp=going), Attendee([email protected], firstName=John, lastName=Doe, rsvp=null)], description=Discussing the fluid-json library., end=2019-03-05T00:45:08.335Z, id=1, start=2019-03-04T22:45:08.339Z, title=fluid-json MeetUp)

(nope, no pretty serialization yet)

Annotation Customization

In this section are a few examples on how JSON codec generation can be customized.

The full documentation on all annotations and properties controlling the JSON codec generation can be found in the KDoc for @Json.

Collect all generated codecs in one codec provider

All codecs in your module generated by annotation processing can automatically be added to a single codec provider which makes using these codecs much simpler.

@Json.CodecProvider
interface MyCodecProvider : JsonCodecProvider<JsonCodingContext>

fun main() {
	val parser = JsonCodingParser.builder()
		.decodingWith(JsonCodecProvider.generated(MyCodecProvider::class))
		.build()
	//
}

Customize the generated codec

@Json(
	codecName = "MyCoordinateCodec",            // customize the JsonCodec's name
	codecPackageName = "some.other.location",          // customize the JsonCodec's package
	codecVisibility = Json.CodecVisibility.publicRequired  // customize the JsonCodec's visibility
)
data class GeoCoordinate2(
	val latitude: Double,
	val longitude: Double
)

Customize what constructor is used for decoding

@Json(
	decoding = Json.Decoding.annotatedConstructor  // require one constructor to be annotated explicitly
)
data class GeoCoordinate3(
	val altitude: Double,
	val latitude: Double,
	val longitude: Double
) {

	@Json.Constructor
	constructor(latitude: Double, longitude: Double) : this(
		altitude = -1.0,
		latitude = latitude,
		longitude = longitude
	)
}

// input:  {"latitude":50.051961,"longitude":14.431521}
// output: {"altitude":-1.0,"latitude":50.051961,"longitude":14.431521}

Customize what properties are used for encoding (opt-in)

@Json(
	encoding = Json.Encoding.annotatedProperties  // only encode properties annotated explicitly
)
data class User(
	@Json.Property val id: String,
	@Json.Property val name: String,
	val passwordHash: String
)

// input:  {"id":1,"name":"Some User","passwordHash":"123456"}
// output: {"id":1,"name":"Some User"}

Customize what properties are used for encoding (opt-out)

@Json
data class User(
	val id: String,
	val name: String,
	@Json.Excluded val passwordHash: String
)

// input:  {"id":1,"name":"Some User","passwordHash":"123456"}
// output: {"id":1,"name":"Some User"}

Encode extension properties

@Json
data class Person(
	val firstName: String,
	val lastName: String
)

@Json.Property
val Person.name
	get() = "$firstName $lastName"

// input:  {"firstName":"Marc","lastName":"Knaup"}
// output: {"firstName":"Marc","lastName":"Knaup","name":"Marc Knaup"}

Customize JSON property names

Some prefer it that way ¯\_(ツ)_/¯.

@Json
data class Person(
	@Json.Property("first_name") val firstName: String,
	@Json.Property("last_name") val lastName: String
)

// input/input: {"first_name":"John","last_name":"Doe"}

Inline a single value

@Json(
	representation = Json.Representation.singleValue  // no need to wrap in a structured JSON object
)
class EmailAddress(val value: String)

// input:  "[email protected]"
// output: "[email protected]"

Prevent encoding completely

@Json(
	encoding = Json.Encoding.none,              // prevent encoding altogether
	representation = Json.Representation.singleValue  // no need to wrap in a structured JSON object
)
class Password(val secret: String)

// input:  "123456"
// output: not possible

Prevent decoding completely

@Json(
	decoding = Json.Decoding.none  // prevent decoding altogether
)
class Response<Result>(val result: result)

// input:  not possible
// output: {"result":…}

Add properties depending on the context

@Json(
	decoding = Json.Decoding.none,                // prevent decoding altogether
	encoding = Json.Encoding.annotatedProperties  // only encode properties annotated explicitly
)
data class User(
	@Json.Property val id: String,
	@Json.Property val name: String,
	val emailAddress: String
)


@Json.CustomProperties  // function will be called during encoding
fun JsonEncoder<MyContext>.writeCustomProperties(value: User) {
	if (context.authenticatedUserId == value.id)
		writeMapElement("emailAddress", value = value.emailAddress)
}


@Json.CodecProvider
interface MyCodecProvider : JsonCodecProvider<MyContext>


data class MyContext(
	val authenticatedUserId: String?
) : JsonCodingContext


fun main() {
	val serializer = JsonCodingSerializer
		.builder(MyContext(authenticatedUserId = "5678"))
		.encodingWith(JsonCodecProvider.generated(MyCodecProvider::class))
		.build()

	println(serializer.serializeValue(listOf(
		User(id = "1234", name = "Some Other User", emailAddress = "[email protected]"),
		User(id = "5678", name = "Authenticated User", emailAddress = "[email protected]")
	)))
}

// input:  not possible
// output: [{"id":"1234","name":"Some Other User"},{"id":"5678","name":"Authenticated User","emailAddress":"[email protected]"}]

Annotate types without having the source code

If a type is not part of your module you can still annotate it indirectly in order to automatically generate a codec for it. Note that this currently does not work correctly if the type has internal properties or an internal primary constructor.

@Json.CodecProvider(
	externalTypes = [
		Json.ExternalType(Triple::class, Json(
			codecVisibility = Json.CodecVisibility.publicRequired
		))
	]
)
interface MyCodecProvider : JsonCodecProvider<JsonCodingContext>

Examples

Have a look at the examples directory. If you've checked out this project locally then you can run them directly from within IntelliJ IDEA.

Manual Coding

Instead of using annotations to generate codecs, JSON can be written either directly using low-level APIs or by manually creating codecs to decode and encode classes from and to JSON.

Simple Parsing

= JsonParser.default.parseValue("""{ "hello": "world", "test": 123 }""")

// returns a value like this:
mapOf(
	"hello" to "world",
	"test" to 123
)

You can also accept a null value by using parseValueOrNull instead.

Full example

Simple Serializing

JsonSerializer.default.serializeValue(mapOf(
	"hello" to "world",
	"test" to 123
))

// returns a string:
// {"hello":"world","test":123}

Full example

Using Reader and Writer

While the examples above parse and return JSON as String you can also use Reader and Writer:

val reader: Reader = …
… = JsonParser.default.parseValue(source = reader)

val writer: Writer =JsonSerializer.default.serializeValue(…, destination = writer)

Full example for Reader and for Writer

Parsing Lists and Maps

You can also parse lists and maps in a type-safe way directly. Should it not be possible to parse the input as the requested Kotlin type a JsonException is thrown. Note that this requires the -coding library variant.

val parser = JsonCodingParser.default

parser.parseValueOfType<List<*>>(…)              // returns List<*>
parser.parseValueOfType<List<String?>>(…)        // returns List<String?>
parser.parseValueOfType<Map<*, *>>(…)             // returns Map<*,*>
parser.parseValueOfType<Map<String, String?>>(…)  // returns Map<String,String?>

Note that you can also specify non-nullable String instead of nullable String?. But due to a limitation of Kotlin and the JVM the resulting list/map can always contain null keys and values. This can cause an unexpected NullPointerException at runtime if the source data contains nulls.

Full example for Lists and for Maps

Streaming Parser

JsonReader provides an extensive API for reading JSON values from a Reader.

val input = StringReader("""{ "data": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] }""")
JsonReader.build(input).use { reader ->
	reader.readFromMapByElementValue { key ->
		println(key)

		readFromListByElement {
			println(readInt())
		}
	}
}

Full example using higher-order functions and using low-level functions

Streaming Writer

JsonWriter provides an extensive API for writing JSON values to a Writer.

val output = StringWriter()
JsonWriter.build(output).use { writer ->
	writer.writeIntoMap {
		writeMapElement("data") {
			writeIntoList {
				for (value in 0 .. 10) {
					json.writeInt(value)
				}
			}
		}
	}
}

Full example using higher-order functions and using low-level functions

Type Encoder Codecs

While many basic Kotlin types like String, List, Map and Boolean are serialized automatically to their respective JSON counterparts you can easily add support for other types. Just write a codec for the type you'd like to serialize by implementing JsonEncoderCodec and pass an instance to the builder of either JsonCodingSerializer (high-level API) or JsonEncoder (streaming API).

Codecs in turn can write other encodable values and JsonEncoder will automatically look up the right codec and use it to serialize these values.

If your codec encounters an inappropriate value which it cannot encode then it will throw a JsonException in order to stop the serialization process.

Because JsonEncoderCodec is simply an interface you can use AbstractJsonEncoderCodec as base class for your codec which simplifies implementing that interface.

data class MyType(…)

object MyTypeCodec : AbstractJsonEncoderCodec<MyType, JsonCodingContext>() {

	override fun JsonEncoder<JsonCodingContext>.encode(value: MyType) {
		// write JSON for `value` directly using the encoder (the receiver)
	}
}

Full example

Type Decoder Codecs

While all JSON types are parsed automatically using appropriate Kotlin counterparts like String, List, Map and Boolean you can easily add support for other types. Just write a codec for the type you'd like to parse by implementing JsonDecoderCodec and pass an instance to the builder of either JsonCodingParser (high-level API) or JsonDecoder (streaming API).

Codecs in turn can read other decodable values and JsonDecoder will automatically look up the right codec and use it to parse these values.

If your codec encounters inappropriate JSON data which it cannot decode then it will throw a JsonException in order to stop the parsing process.

Because JsonDecoderCodec is simply an interface you can use AbstractJsonDecoderCodec as base class for your codec which simplifies implementing that interface.

data class MyType(…)

object MyTypeCodec : AbstractJsonDecoderCodec<MyType, JsonCodingContext>() {

	override fun JsonDecoder<JsonCodingContext>.decode(valueType: JsonCodingType<MyType>): MyType {
		// read JSON using and create an instance of `MyType` using decoder (the receiver)
	}
}

A JsonDecoderCodec can also decode generic types. The instance passed to JsonCodingType contains information about generic arguments expected by the call which caused this codec to be invoked. For List<Something> for example a single generic argument of type Something would be reported which allows for example the list codec to serialize the list value's directly as Something using the respective codec.

Full example

Type Codecs

If you want to be able to encode and decode the same type you can implement the interface JsonCodec which in turn extends JsonEncoderCodec and JsonDecoderCodec. That way you can reuse the same codec class for both, encoding and decoding.

Because JsonCodec is simply an interface you can use AbstractJsonCodec as base class for your codec which simplifies implementing that interface.

Full example

Coding and Streaming

You can use encoding and decoding codecs not just for high-level encoding and decoding using JsonCodingSerializer and JsonCodingParser but also for streaming-based encoding and decoding using JsonEncoder and JsonDecoder.

Full example

Thread Safety

All implementations of JsonParser, JsonSerializer, JsonCodecProvider as well as all codecs provided by this library are thread-safe and can be used from multiple threads without synchronization. It's strongly advised, though not required, that custom implementations are also thread-safe by default.

All other classes and interfaces are not thread-safe and must be used with appropriate synchronization in place. It's recommended however to simply use a separate instance per thread and not share these mutable instances at all.

Error Handling

Errors occurring during I/O operations in the underlying Reader or Writer cause an IOException.
Errors occurring due to unsupported or mismatching types, malformed JSON or misused API cause a subclass of JsonException being thrown.

Since in Kotlin every method can throw any kind of exception it's recommended to simply catch Exception when encoding or decoding JSON - unless handling errors explicitly is not needed in your use-case. This is especially important if you parse JSON data from an unsafe source like a public API.

Default JsonException subclasses

Exception Usage
JsonException.Parsing Thrown when a JsonReader was used improperly, i.e. it's a development error.
JsonException.Serialization Thrown when a JsonWriter was used improperly, e.g. if it would result in malformed JSON.
JsonException.Schema Thrown when a JsonReader or JsonDecoder reads data in an unexpected format, i.e. them schema of the JSON data is wrong.
JsonException.Syntax Thrown when a JsonReader reads data which is not properly formatted JSON.

Ktor Client

You can use this library with JsonFeature of Ktor Client.

build.gradle.kts:

dependencies {
	implementation("io.fluidsonic.json:fluid-json-ktor-client:1.2.1")
}

Setting up your HttpClient:

val client = HttpClient(…) {
	install(JsonFeature) {
		serializer = FluidJsonSerializer(
			parser = JsonCodingParser
				.builder()
				.decodingWith(…)
			.build(),
		serializer = JsonCodingSerializer
			.builder()
			.encodingWith(…)
		.build()
		)
	}
}

Modules

Module Usage
fluid-json-annotation-processor @Json-based JsonCodec creation using kapt
fluid-json-annotations contains @Json annotations
fluid-json-basic low-level API with JsonReader/JsonParser and JsonWriter/JsonSerializer
fluid-json-coding JsonCodec-based parsing and serialization using JsonDecoder/JsonCodingParser and JsonEncoder/JsonCodingSerializer
fluid-json-coding-jdk8 additional JsonCodecs for commonly used Java 8 types on top of fluid-json-coding
fluid-json-ktor-client plugs in JsonCodingParser/JsonCodingSerializer to ktor-client using its JsonSerializer

Testing

This library is tested automatically using extensive unit tests. Some parser tests are imported directly from JSONTestSuite (kudos to Nicolas Seriot for that suite).

You can run the tests manually using Tests run configuration in IntelliJ IDEA or from the command line by using:

./gradlew check

Type Mapping

Basic Types

Encoding

The default implementations of JsonWriter and JsonSerializer encode Kotlin types as follows:

Kotlin JSON Remarks
Array<*> array<*>
Boolean boolean
BooleanArray array<boolean>
Byte number
ByteArray array<number>
Char string
CharArray array<string>
Collection<E> array<*> using decoder/encoder for E
Double number must be finite
DoubleArray array<number>
Float number must be finite
FloatArray array<number>
Int number
IntArray array<number>
Iterable<E> array<*> using decoder/encoder for E
List<E> array<*> using decoder/encoder for E
Long number
LongArray array<number>
Map<K,V> object<string,*> key must be String, using decoders/encoders for K and V
Number number unless matched by subclass; encodes as toDouble()
Sequence<E> array<*> using decoder/encoder for E
Set<E> array<*> using decoder/encoder for E
Short number
ShortArray array<number>
String string
null null

Decoding

The default implementations of JsonReader and JsonParser decode JSON types as follows:

JSON Kotlin Remarks
array<*> List<*>
boolean Boolean
null null
number Int if number doesn't include . (decimal separator) or e (exponent separator) and fits into Int
number Long if number doesn't include . (decimal separator) or e (exponent separator) and fits into Long
number Double otherwise
object<string,*> Map<String,*>
string String

Extended Types

The following classes of the can also be decoded and encoded out of the box.
For types in the java.time package the -coding-jdk8 library variant must be used.

Kotlin JSON Remarks
CharRange { "start": …, "endInclusive": … } using string value
ClosedRange<C> { "start": …, "endInclusive": … } using decoder/encoder for C
Enum string uses .toString() and converts to lowerCamelCase (can be configured)
DayOfWeek string "monday", …, "friday"
Duration string using .parse() / .toString()
Instant string using .parse() / .toString()
IntRange { "start": …, "endInclusive": … } using number values
LocalDate string using .parse() / .toString()
LocalDateTime string using .parse() / .toString()
LocalTime string using .parse() / .toString()
LongRange { "start": …, "endInclusive": … } using number values
MonthDay string using .parse() / .toString()
Month string "january", …, "december"
OffsetDateTime string using .parse() / .toString()
OffsetTime string using .parse() / .toString()
Period string using .parse() / .toString()
Year int using .value
YearMonth string using .parse() / .toString()
ZonedDateTime string using .parse() / .toString()
ZoneId string using .of() / .id
ZoneOffset string using .of() / .id

Architecture

  • JsonReader/JsonWriter are at the lowest level and read/write JSON as a stream of JsonTokens:
    • part of -basic library variant
    • character-level input/output
    • validation of read/written syntax
    • one instance per parsing/serializing (maintains state & holds reference to Reader/Writer)
  • JsonParser/JsonSerializer are built on top of JsonReader/JsonWriter and read/write a complete JSON value at once.
    • part of -basic library variant
    • completely hides usage of underlying JsonReader/JsonWriter
    • encoding is performed using the actual type of values to be encoded
    • decoding is performed using the type expected by the caller of JsonParser's parse… methods and only available for basic types
    • instance can be reused and creates one JsonReader/JsonWriter per parsing/serialization invocation
    • ease of use is important
  • JsonDecoder/JsonEncoder are built on top of JsonReader/JsonWriter and decode/encode arbitrary Kotlin types from/to a stream of JsonTokens:
    • part of -coding library variant
    • most read/write operations are forwarded to the underlying JsonReader/JsonWriter
    • some read/write operations are intercepted by JsonEncoder to encode compatible types using codecs
    • implementations provided by JsonDecoderCodecs and JsonEncoderCodecs
    • inspired by MongoDB's Codec and CodecRegistry
    • one instance per parsing/serialization invocation (holds reference to JsonReader/JsonWriter)
  • JsonCodingParser/JsonCodingSerializer are built on top of JsonDecoder/JsonEncoder and read/write a complete JSON value at once.
    • part of -coding library variant
    • completely hides usage of underlying JsonDecoder/JsonEncoder
    • encoding is performed using the actual type of values to be encoded using a matching JsonEncoderCodec implementation
    • decoding is performed using the type expected by the caller of JsonParser's parse… methods and a matching JsonDecoderCodec implementation
    • instance can be reused and creates one JsonDecoder/JsonEncoder per parsing/serialization invocation
    • ease of use is important

Most public API is provided as interfaces in order to allow for plugging in custom behavior and to allow easy unit testing of code which produces or consumes JSON.

The default implementations of JsonDecoder/JsonEncoder use a set of pre-defined codecs in order to support decoding/encoding various basic Kotlin types like String, List, Map, Boolean and so on. Codecs for classes which are available only since Java 8 are provided by the -coding-jdk8 library variant.

Recursive vs. Non-Recursive

While codec-based decoding/encoding has to be implemented recursively in order to be efficient and easy to use it's sometimes not desirable to parse/serialize JSON recursively. For that reason the default container codecs like MapJsonCodec also provide a nonRecursive codec. Since they read/write a whole value at once using JsonReader's/JsonWriter's primitive read*/write* methods they will not use any other codecs and thus don't support encoding or decoding other non-basic types.

JsonCodingParser.nonRecursive and JsonCodingSerializer.nonRecursive both operate on these codecs and are thus a non-recursive parser/serializer.

Classes and Interfaces

Type Description
AbstractJsonCodec Abstract base class which simplifies implementing JsonCodec.
AbstractJsonDecoderCodec Abstract base class which simplifies implementing JsonDecoderCodec.
AbstractJsonEncoderCodec Abstract base class which simplifies implementing JsonEncoderCodec.
DefaultJsonCodecs Contains lists of default codecs which can be used when constructing custom JsonCodecProviders.
JsonCodec Interface for classes which implement both, JsonEncoderCodec and JsonDecoderCodec. Also simplifies creating such codecs.
JsonCodecProvider Interface for classes which when given a JsonCodingType (for decoding) or KClass (for encoding) return a codec which is able to decode/encode values of that type.
JsonCodingContext Interface for context types. Instances of context types can be passed to JsonParser, JsonSerializer, JsonDecoder and JsonEncoder. They in turn can be used by custom codecs to help decoding/encoding values if needed.
JsonCodingParser Interface for high-level reusable JSON parsers with codec providers and context already configured.
JsonCodingSerializer Interface for high-level reusable JSON serializers where codec providers and context are already configured.
JsonCodingType Roughly describes a Kotlin type which can be decoded from JSON. It includes relevant generic information which allows decoding for example List<Something> instead of just List<*>. Also known as type token).
JsonDecoder Interface which extends JsonReader to enable reading values of any Kotlin type from JSON using JsonCodecProviders for type mapping.
JsonDecoderCodec Interface for decoding a value of a specific Kotlin type using a JsonDecoder.
JsonEncoder Interface which extends JsonWriter to enable writing values of any Kotlin type as JSON using JsonCodecProviders for type mapping.
JsonEncoderCodec Interface for encoding a value of a specific Kotlin type using a JsonEncoder.
JsonException Exception base class which is thrown whenever JSON cannot be written or read for non-IO reasons (e.g. malformed JSON, wrong state in reader/writer, missing type mapping).
JsonParser Interface for high-level reusable JSON parsers which support only basic types.
JsonReader Interface for low-level JSON parsing on a token-by-token basis.
JsonSerializer Interface for high-level reusable JSON serializers which support only basic types.
JsonToken Enum containing all types of tokens a JsonReader can read.
JsonWriter Interface for low-level JSON serialization on a token-by-token basis.
*Codec The various codec classes are concrete codecs for common Kotlin types.

Future Planning

This is on the backlog for later consideration, in no specific order:

License

Apache 2.0


Awesome Kotlin

Comments
  • Add support for Validation

    Add support for Validation

    Usually on deserialization we want to collect all validation errors and return them in response to the client. In order to catch all error the validation should be done before object creation.

    If it will be done after object creation, in init block, than validation will be separated in 2 phases, first one will be validation regarding non-nullable field, because we cannot create objects if they have non-nullable properties. The second one will be to do structural validation: min/max values, length, regexp pattern, etc.

    Example class Data(val name: String, val age: Int, val email: String) when we send a json {"email": "[email protected]", "name": "A"} we should return a response where to indicate that age can not be null and name size must be greater than 2, at once to send all the errors.

    Also, structural validation will be great to do based on annotations and not in init block, something like JSR 303. This will be useful for documentation generation, it is far easier to generate based on annotations.

    How I see the flow:

    1. Read annotations and generate validation rules. Can be done by 3rd party library. As result Map<Class, List>
    2. Parse json as a map
    3. Apply validation rules. Validation rules can be passed as parameter in configuration.
    4. Create object
    opened by raderio 7
  • Polymorphism support

    Polymorphism support

    sealed class Shape
    class Circle(val radius: Double) : Shape
    class Rectangle(val width: Double) : Shape
    

    There are 2 types of polymorphism:

    1. Polymorphism by field value, aka discriminator 1.1 Discriminator embedded in object
    [ { "type": "CIRCLE", "radius": 10.0 }, { "type": "RECTANGLE", "width": 20.0 } ]
    

    1.2 Discriminator external

    [ { "type": "CIRCLE", "data": { "radius": 10.0 } }, { "type": "RECTANGLE", "data": { "width": 20.0 } } ]
    
    1. Polymorphism by filed name, aka union, or anyOf from Swagger
    [ { "radius": 10.0 }, { "width": 20.0 } ]
    

    Because only Circle class has radius field, first object from list will be deserialized in Circle class.

    Also should be possible to do nested deserialization(multi level).

    Please add support for all this cases.

    opened by raderio 1
  • Split up into multiple libraries

    Split up into multiple libraries

    • [x] separate tests
    • [x] properly define auto-closing of streams
    • [x] clean up API
    • [x] decide on final library naming
    • [x] double-check Gradle project configurations
    • [x] double-check test setup
    • [x] double-check Travis CI setup
    • [x] set up Maven Central publishing
    opened by fluidsonic 1
  • Add codecs for direct object <-> JSON conversion (like MongoDB Java Driver)

    Add codecs for direct object <-> JSON conversion (like MongoDB Java Driver)

    • [ ] Add checks to make sure that no coder messes up the node hierarchy.
    • [ ] Properly plan en-/decoding of map keys.
    • [ ] Add encoding/decoding functionality for map keys.
    • [ ] Add extended standard codecs, e.g. for java.time.*
    opened by fluidsonic 0
  • Add stream-based parser and serializer (like MongoDB Java Driver)

    Add stream-based parser and serializer (like MongoDB Java Driver)

    • [x] Decide whether to treat map keys as values or whether to add special token type and readKey() method
    • [ ] Parse double directly without temporary string.
    • [ ] Add tests for character indexes in exception messages.
    • [ ] Add tests for keypaths in exception messages.
    • [ ] Should we wrap IOException, InterruptedException (all Exceptions?) in JSONException?
    • [ ] Serialize numbers without temporary string.
    • [ ] Improve performance by using Input/OutputStream directly.
    • [ ] Support Appendable.
    • [ ] Support Readable.
    • [ ] Support reading & writing Byte and Short.
    • [ ] Support reading & writing Char.
    • [ ] Allow peeking whether next number has decimal point or exponent in JSONReader.
    • [ ] Properly close readers & writers in tests.
    • [ ] Add overloads for readString and writeString which operate on CharArray and alike.
    • [ ] Add options (e.g. handling of unserializable values, max nesting, max number length, max string length)
    • [ ] Add dedicated tests for TextInput.
    opened by fluidsonic 0
  • fix codec generation for properties whose type references a generic type of the value class

    fix codec generation for properties whose type references a generic type of the value class

    @JSON
    class ClientCommand<out Result : Any>(
        val resultClass: KClass<out Result>
    )
    
    @JSON
    class ClientCommandRequest<out Result : Any>(
        val command: ClientCommand<Result>
    )
    
    opened by fluidsonic 0
Releases(1.3.0)
  • 1.3.0(Feb 1, 2022)

  • 1.1.0(Jul 29, 2020)

  • 1.0.0(Oct 19, 2019)

  • 0.9.18(Apr 3, 2019)

    Changes

    • JSONCodingParser interface is now generic with a JSONCodingContext parameter
    • JSONCodingParser interface allows creating decoders manually
    • JSONCodingParser interface distinguishes between …OrNull and normal value parsing
    • JSONDecoderCodecs can now decode null values

    Migration

    • replace variable: JSONCodingParser with variable: JSONCodingParser<*>
    Source code(tar.gz)
    Source code(zip)
  • 0.9.17(Mar 15, 2019)

  • 0.9.16(Mar 13, 2019)

  • 0.9.15(Mar 8, 2019)

  • 0.9.14(Mar 7, 2019)

    Changes

    • added support for optional constructor parameters when decoding with @JSON
    • added default codec for Set
    • fixed issues with @JSON.ExternalType and inline classes wrapping primitive types
    • fixed incorrect variance in JSONDecoderCodec.decode()

    Migration

    Search & replace in your project:

    decode(valueType: JSONCodingType<in 
    

    -->

    decode(valueType: JSONCodingType<
    
    Source code(tar.gz)
    Source code(zip)
  • 0.9.13(Mar 5, 2019)

    Changes

    • 🎉 annotation-based JSONCodec and JSONCodecProvider generation using @JSON
    • added default codec for Collection<*>
    • added ability to generate JSONCodingType for generic types (e.g. jsonCodingType(List::class, String::class))
    Source code(tar.gz)
    Source code(zip)
  • 0.9.12(Feb 5, 2019)

    Fixes

    • fixed incorrect type projection in JSONCodecProvider
    • disabled all contracts in public functions until KT-29510 & KT-29614 are fixed

    Features

    • added JSONCodecProvider.factory { … } and JSONCodecProvider.factory<BaseClass> { … }
    • added support for decoding and encoding enums with lowercaseCamelCase transformation on each value's .toString() by default, i.e. EnumClass.SOME_ENUM_VALUE <-> "someEnumValue" Use a different instance of EnumJSONCodecProvider in your codec provider to change the transformation.

    Housekeeping

    • switched license from MIT to Apache 2.0
    • deprecated JSONCodecProvider.of()
    • added JSONCodecProvider()

    Migration from 0.9.11

    • change JSONCodecProvider.of(…) to JSONCodecProvider(…) Note that JSONCodecProvider(…) doesn't add default codecs anymore automatically so if you rely on that then use this replacement: JSONCodecProvider(JSONCodecProvider(…), JSONCodecProvider.extended). The builders of JSONEncoder and JSONDecoder still add default codecs automatically.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.11(Feb 1, 2019)

  • 0.9.10(Jan 27, 2019)

    Features

    • JSONException is now abstract and uses more specific subclasses and reports JSON path and parser offset relevant for each error
    • added JSONReader.readOrNull { … }
    • added JSONWriter.writeOrNull(value) { … }
    • added JSONPath, accessible using JSONReader.path and JSONWriter.path and JSONException.path
    • added JSONReader.offset
    • added JSONReader.isolatedValueRead and JSONWriter.isolatedValueWrite which area already used behind the scene to catch common mistakes when using these classes, e.g. when implementing codecs

    Housekeeping

    • removed duplicate method JSONReader.readElementsFromMap (duplicates .readFromMapByElementValue)
    • marked many lambda parameters as crossinline to prevent inconsistent states which cause hard-to-spot mistakes

    Migration from 0.9.9

    JSONException is now abstract. Use the appropriate .Parsing, .Schema, .Syntax or .Serialization subclass as explained in Error Handling.

    There are also shortcuts for common errors in codecs and which throw appropriate exceptions including parsing offset and JSON path:

    • JSONDecoder.invalidPropertyError(property: String, details: String)
    • JSONDecoder.invalidValueError(details: String)
    • JSONDecoder.missingPropertyError(property: String)
    • JSONDecoder.parsingError(message: String)
    • JSONDecoder.schemaError(message: String)
    • JSONEncoder.serializationError(message: String)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.9(Jan 20, 2019)

    Features

    Housekeeping

    • [239738b] encoder/decoder in Codecs are now receiver parameters

    Migration from 0.9.8

    in JSONDecoderCodecs:

    override fun decode(valueType: JSONCodingType<in Value>, decoder: JSONDecoder<JSONCodingContext>) =
        decoder.read…()
    

    override fun JSONDecoder<JSONCodingContext>.decode(valueType: JSONCodingType<in Value>) =
        read…()
    

    in JSONEncoderCodecs:

    override fun encode(value: Value, encoder: JSONEncoder<JSONCodingContext>) {
        encoder.write…(value)
    }
    

    override fun JSONEncoder<JSONCodingContext>.encode(value: Value) {
        write…(value)
    }
    
    Source code(tar.gz)
    Source code(zip)
  • 0.9.8(Dec 13, 2018)

  • 0.9.7(Nov 29, 2018)

    Features

    • Java 7 support

    Housekeeping

    • [bb087cd] updated API and separated basic & coding

    Migration from 0.9.6

    • change the artifact id from fluid-json to fluid-json-coding-jdk8, fluid-json-coding or fluid-json-basic depending on needed functionality and Java version
    • change jsonCodableType to jsonCodingType
    • change JSONCodableType to JSONCodingType
    • change JSONCodableTypeReference to JSONCodingTypeReference
    • change JSONCoderContext to JSONCodingContext
    • change JSONParser to JSONCodingParser (unless you only need the basic functionality)
    • change JSONSerializer to JSONCodingSerializer (unless you only need the basic functionality)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.6(Nov 28, 2018)

  • 0.9.5(Nov 27, 2018)

  • 0.9.4(Nov 27, 2018)

  • 0.9.3(Nov 27, 2018)

  • 0.9.2(Nov 27, 2018)

  • 0.9.1(Nov 13, 2017)

    Fixes

    • [f308baf] Fixed buffer corruption when parsing large input

    Features

    • [4bb13b4] Added JSONParser/JSONSerializer.nonRecursive and made .default recursive
    • [1c5b40e] Added codecs for java.time classes and cleaned up codec setup logic
    • [a306a10] Switched from kotlin-stdlib to kotlin-stdlib-jre8
    • [c3bda8e] Added ability to create JSONCodableType from KClass
    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Nov 2, 2017)

Owner
Marc Knaup
❤️ tech / startups / crypto / travel
Marc Knaup
Annotation Processing Library. Generates proxy class on top of interface/abstract class, that allows to intercept calls. Also known as a design pattern: proxy, delegate, interceptor.

1. AutoProxy Annotation Processing Library. Generates proxy class on top of interface/abstract class, that allows to intercept calls. Also known as a

Oleksandr 19 Nov 24, 2022
StoryGen - A simple story generator (or it will be eventually) to learn Kotlin

StoryGen A simple story generator (or it will be eventually) to learn Kotlin. To

Markus Reinke 0 Jan 7, 2022
Mirai-device-generator - Mirai Device Generator with kotlin

Mirai Device Generator Mirai DeviceInfo 生成器 作为插件运行时会提供 BotConfigurationAlterer 服

cssxsh 46 Jan 1, 2023
Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

null 8 Dec 15, 2022
Android Material Json Form Wizard is a library for creating beautiful form based wizards within your app just by defining json in a particular format.

Android Json Wizard Android Json Wizard is a library for creating beautiful form based wizards within your app just by defining json in a particular f

Vijay Rawat 355 Nov 11, 2022
Lightweight service for creating standalone mock, written in pure Kotlin with Netty container.

MockService The lightweight service for creating a standalone mock, written in pure Kotlin with Netty container. The service allows getting config fil

null 2 Oct 28, 2021
Images grid JSON | Сетка изображений JSON

Images grid JSON | Сетка изображений JSON Задача Разработать приложение: Приложение должно получать JSON-список ссылок на изображения с сервера по адр

Sefo Notasi 2 Apr 25, 2022
Dynamic-UI-From-JSON - A Sample Android app to show dynamic UI generation from Json

Dynamic UI from JSON Functionality The app's functionality includes: The app gen

Rafsan Ahmad 12 Dec 16, 2022
A pure-Kotlin library for bots to interface with Revolt

RevoltKt A pure-Kotlin library for bots to interface with Revolt Sample Usage import me.maya.revolt.defaultClientBuilder import me.maya.revolt.events.

Maya 8 May 20, 2022
A basic chart written by kotlin. Support animation loading, touch event monitoring and JSON data.

A basic chart written by kotlin. Support animation loading, touch event monitoring and JSON data.

null 2 Dec 21, 2022
:sound: [Android Library] Easily generate pure audio tone of any frequency in android

Android library to easily generate audio tone in android. Generating pure tone of an specific frequency was never that easy. ZenTone does all the heav

Nishant Srivastava 102 Dec 19, 2022
Android & Pure Java library for sound manipulation

soundtransform Android & Pure Java library to shape a voice with an instrument. Table of Contents How to use the library FluentClient FluentClient sam

LiBe 42 Sep 12, 2021
QRCode Generator implemented in pure Kotlin

Creating QRCodes in Kotlin and Java is harder than it should be. QRCode-Kotlin aims to bring a simple, straightforward and customizable way to create QRCodes into the JVM domain, especially in the backend.

Rafael Lins 42 Dec 28, 2022
sql-delight example, a plugin by Square which is pure kotlin and it is useful in kmm

Sql-Delight-Example01 Developed by Mahdi Razzaghi Ghaleh first example of sql-delight What is SqlDelight? Kotlin Multiplatform is one of the most inte

rq_mehdi 0 Jan 24, 2022
Android Word/Letter Tracing SDK is a complet solution to build apps for kids learning in pure kotlin

Android Word/Letter Tracing SDK is a complet solution to build apps for kids learning in pure kotlin. It supports all kind of shapes and all language letters e.g. english, arabic, Urdu, hindi etc.

null 9 Oct 16, 2022
A pure Kotlin/Multiplatform implementation of group operations on Curve25519.

curve25519-kotlin A pure Kotlin/Multiplatform implementation of group operations on Curve25519. Gradle Kotlin DSL: dependencies { implementation("

Andrey Pfau 9 Dec 22, 2022
Pure Java code generation tool for generating a fully functional ContentProvider for Android.

RoboCoP RoboCoP is a Java library that can generate a fully-functional ContentProvider from a simple JSON schema file. Get the latest version from our

Rain 246 Dec 29, 2022
Nuwa, pure java implementation, can hotfix your android application.

Nuwa Nuwa is a goddess in ancient Chinese mythology best known for repairing the pillar of heaven. With this Nuwa project,you can also have the repair

Jason Ross 3k Dec 17, 2022
Template (pure) for KMM application with DI support

KMM di template Template (pure) for KMM application with DI support. Uses Multiplatform-DI for Dependency Injection Features Common architecture (VIP)

Anna Zharkova 8 Oct 18, 2022
Animated dark mode toggle button with Android & Pure Java. ☕

Dark-Toggle-Button Animated dark mode toggle button for Android Java. ☕ converted from kotlin to Java 201 lines: DarkToggleButton.java Android Demo In

knziha 2 Mar 13, 2022