├── annotation-processor ├── test-cases │ ├── 1 │ │ ├── input │ │ │ ├── json │ │ │ │ ├── codecPackageName │ │ │ │ │ ├── DefaultRoot.kt │ │ │ │ │ ├── Default.kt │ │ │ │ │ ├── AutomaticRoot.kt │ │ │ │ │ ├── CustomRoot.kt │ │ │ │ │ ├── Automatic.kt │ │ │ │ │ └── Custom.kt │ │ │ │ ├── classes │ │ │ │ │ ├── Class.kt │ │ │ │ │ ├── DataClass.kt │ │ │ │ │ ├── InlineClass.kt │ │ │ │ │ ├── Object.kt │ │ │ │ │ └── GenericClass.kt │ │ │ │ ├── decoding │ │ │ │ │ ├── DefaultObject.kt │ │ │ │ │ ├── AutomaticObject.kt │ │ │ │ │ ├── DefaultPrimaryConstructor.kt │ │ │ │ │ ├── AutomaticPrimaryConstructor.kt │ │ │ │ │ ├── None.kt │ │ │ │ │ ├── PrimaryConstructor.kt │ │ │ │ │ ├── DefaultSecondaryConstructorPrimaryExcluded.kt │ │ │ │ │ ├── DefaultSecondaryConstructorPrimaryInaccessible.kt │ │ │ │ │ ├── DefaultSecondaryConstructorPrimaryNotPresent.kt │ │ │ │ │ ├── DefaultAnnotatedConstructor.kt │ │ │ │ │ ├── AutomaticSecondaryConstructorPrimaryExcluded.kt │ │ │ │ │ ├── AutomaticSecondaryConstructorPrimaryInaccessible.kt │ │ │ │ │ ├── AutomaticSecondaryConstructorPrimaryNotPresent.kt │ │ │ │ │ ├── AnnotatedConstructor.kt │ │ │ │ │ ├── ConstructorParameterOrder.kt │ │ │ │ │ └── AutomaticAnnotatedConstructor.kt │ │ │ │ ├── codecName │ │ │ │ │ ├── Default.kt │ │ │ │ │ ├── Automatic.kt │ │ │ │ │ └── Custom.kt │ │ │ │ ├── codecVisibility │ │ │ │ │ ├── DefaultInternal.kt │ │ │ │ │ ├── Internal.kt │ │ │ │ │ ├── Public.kt │ │ │ │ │ ├── AutomaticPublic.kt │ │ │ │ │ ├── AutomaticInternal.kt │ │ │ │ │ └── AutomaticInternalForContainedClass.kt │ │ │ │ ├── representation │ │ │ │ │ ├── DefaultSingleValue.kt │ │ │ │ │ ├── DefaultStructured.kt │ │ │ │ │ ├── SingleValue.kt │ │ │ │ │ ├── Structured.kt │ │ │ │ │ ├── AutomaticStructured.kt │ │ │ │ │ ├── SingleValueNullable.kt │ │ │ │ │ ├── AutomaticSingleValue.kt │ │ │ │ │ ├── StructuredValueGeneric.kt │ │ │ │ │ └── SingleValueGeneric.kt │ │ │ │ └── encoding │ │ │ │ │ ├── None.kt │ │ │ │ │ ├── Default.kt │ │ │ │ │ ├── Automatic.kt │ │ │ │ │ ├── AllProperties.kt │ │ │ │ │ └── AnnotatedProperties.kt │ │ │ ├── property │ │ │ │ ├── serializedName │ │ │ │ │ ├── Default.kt │ │ │ │ │ ├── Custom.kt │ │ │ │ │ └── Automatic.kt │ │ │ │ └── defaultValue │ │ │ │ │ ├── AutomaticGeneric.kt │ │ │ │ │ └── Automatic.kt │ │ │ ├── customProperties │ │ │ │ ├── DifferentPackageExtensions.kt │ │ │ │ ├── SamePackage.kt │ │ │ │ ├── DifferentPackage.kt │ │ │ │ └── CustomContext.kt │ │ │ └── codecProvider │ │ │ │ └── CustomContextCodecProvider.kt │ │ └── output-expected │ │ │ ├── json │ │ │ ├── decoding │ │ │ │ ├── DefaultObjectJsonCodec.kt │ │ │ │ ├── AutomaticObjectJsonCodec.kt │ │ │ │ └── NoneJsonCodec.kt │ │ │ ├── classes │ │ │ │ ├── ObjectJsonCodec.kt │ │ │ │ └── InlineClassJsonCodec.kt │ │ │ ├── representation │ │ │ │ ├── SingleValueJsonCodec.kt │ │ │ │ ├── AutomaticSingleValueJsonCodec.kt │ │ │ │ ├── SingleValueNullableJsonCodec.kt │ │ │ │ └── SingleValueGenericJsonCodec.kt │ │ │ └── encoding │ │ │ │ └── NoneJsonCodec.kt │ │ │ └── externalType │ │ │ ├── KT30280JsonCodec.kt │ │ │ └── KT30280PrimitiveJsonCodec.kt │ ├── 2 │ │ ├── input │ │ │ └── codecProvider │ │ │ │ └── StandardContextCodecProvider.kt │ │ └── output-expected │ │ │ └── codecProvider │ │ │ └── GeneratedStandardContextCodecProvider.kt │ └── .gitignore ├── sources-jvm │ ├── utility │ │ ├── ErrorLogger.kt │ │ ├── TypeResolver.kt │ │ ├── ExecutableElement.kt │ │ ├── AnnotationMirror.kt │ │ ├── Any.kt │ │ ├── MQualifiedTypeName.kt │ │ ├── AnnotatedConstruct.kt │ │ ├── TypeNames.kt │ │ ├── Element.kt │ │ └── KotlinpoetTypeNames.kt │ ├── generation │ │ └── GenerationPhase.kt │ ├── processing │ │ └── ProcessingResult.kt │ └── AnnotationProcessor.kt └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitmodules ├── coding ├── tests-jvm │ ├── utility │ │ ├── Int.kt │ │ ├── Double.kt │ │ ├── codecs │ │ │ ├── TestCoderContext.kt │ │ │ ├── AnyJsonTestDecoderCodec.kt │ │ │ ├── ContextCheckingTestEncoderCodec.kt │ │ │ ├── ContextCheckingTestDecoderCodec.kt │ │ │ ├── YearMonthDayCodec.kt │ │ │ ├── ArrayJsonTestEncoderCodec.kt │ │ │ ├── ContextCheckingTestCodec.kt │ │ │ ├── IterableJsonEncoderTestCodec.kt │ │ │ ├── ListJsonTestDecoderCodec.kt │ │ │ ├── SetJsonTestDecoderCodec.kt │ │ │ ├── CollectionJsonTestCodec.kt │ │ │ ├── MapJsonTestCodec.kt │ │ │ ├── UniverseCodec.kt │ │ │ └── SequenceJsonTestCodec.kt │ │ ├── Expect.kt │ │ ├── Coding.kt │ │ └── Sequence.kt │ ├── model │ │ ├── Universe.kt │ │ ├── Jaeger.kt │ │ ├── Kaiju.kt │ │ └── YearMonthDay.kt │ ├── data │ │ └── basic │ │ │ ├── booleanData.kt │ │ │ ├── charData.kt │ │ │ ├── intArrayData.kt │ │ │ ├── byteArrayData.kt │ │ │ ├── longArrayData.kt │ │ │ ├── shortArrayData.kt │ │ │ ├── arrayData.kt │ │ │ ├── collectionData.kt │ │ │ ├── booleanArrayData.kt │ │ │ ├── charArrayData.kt │ │ │ ├── floatArrayData.kt │ │ │ ├── doubleArrayData.kt │ │ │ ├── setData.kt │ │ │ ├── listData.kt │ │ │ ├── sequenceData.kt │ │ │ ├── plainData.kt │ │ │ ├── mapData.kt │ │ │ ├── mapDataWithNonStringKeys.kt │ │ │ ├── bigDecimalData.kt │ │ │ ├── iterableData.kt │ │ │ ├── stringData.kt │ │ │ ├── intData.kt │ │ │ ├── longData.kt │ │ │ ├── byteData.kt │ │ │ ├── shortData.kt │ │ │ ├── floatData.kt │ │ │ └── doubleData.kt │ ├── CompanionTest.kt │ ├── codecs │ │ ├── EnumJsonCodecProviderTest.kt │ │ ├── IntRangeJsonCodecTest.kt │ │ ├── LongRangeJsonCodecTest.kt │ │ └── CharRangeJsonCodecTest.kt │ ├── AbstractJsonDecoderCodecTest.kt │ ├── AbstractJsonEncoderCodecTest.kt │ ├── dummys │ │ └── DummyJsonReader.kt │ ├── JsonCodingTypeTest.kt │ ├── StandardCodingSerializerRejectTest.kt │ ├── JsonCodingSerializerTest.kt │ ├── JsonEncoderTest.kt │ └── KClassTest.kt ├── sources-jvm │ ├── JsonCodingContext.kt │ ├── codecs │ │ ├── basic │ │ │ ├── ByteArrayJsonCodec.kt │ │ │ ├── CharArrayJsonCodec.kt │ │ │ ├── IntArrayJsonCodec.kt │ │ │ ├── LongArrayJsonCodec.kt │ │ │ ├── FloatArrayJsonCodec.kt │ │ │ ├── ShortArrayJsonCodec.kt │ │ │ ├── DoubleArrayJsonCodec.kt │ │ │ ├── BooleanArrayJsonCodec.kt │ │ │ ├── IntJsonCodec.kt │ │ │ ├── ByteJsonCodec.kt │ │ │ ├── CharJsonCodec.kt │ │ │ ├── LongJsonCodec.kt │ │ │ ├── FloatJsonCodec.kt │ │ │ ├── ShortJsonCodec.kt │ │ │ ├── DoubleJsonCodec.kt │ │ │ ├── NumberJsonCodec.kt │ │ │ ├── StringJsonCodec.kt │ │ │ ├── BooleanJsonCodec.kt │ │ │ ├── ArrayJsonCodec.kt │ │ │ ├── IterableJsonEncoderCodec.kt │ │ │ ├── SetJsonDecoderCodec.kt │ │ │ ├── CollectionJsonCodec.kt │ │ │ ├── ListJsonDecoderCodec.kt │ │ │ ├── NonRecursiveJsonEncoderCodec.kt │ │ │ ├── SequenceJsonCodec.kt │ │ │ ├── AnyJsonDecoderCodec.kt │ │ │ ├── MapJsonCodec.kt │ │ │ ├── NonRecursiveJsonCodec.kt │ │ │ └── NonRecursiveJsonDecoderCodec.kt │ │ ├── extended │ │ │ ├── EnumJsonCodecProvider.kt │ │ │ ├── IntRangeJsonCodec.kt │ │ │ ├── LongRangeJsonCodec.kt │ │ │ └── CharRangeJsonCodec.kt │ │ └── DefaultJsonCodecs.kt │ ├── implementations │ │ ├── StandardCodingParser.kt │ │ ├── StandardCodingSerializer.kt │ │ ├── StandardDecoder.kt │ │ ├── StandardEncoder.kt │ │ ├── AbstractJsonEncoderCodec.kt │ │ ├── AbstractJsonDecoderCodec.kt │ │ └── AbstractJsonCodec.kt │ ├── CodingImplementationsJava7.kt │ ├── CodingImplementationsJava.kt │ ├── JsonCodec.kt │ ├── JsonEncoderCodec.kt │ ├── utility │ │ ├── KType.kt │ │ └── KClass.kt │ └── JsonDecoderCodec.kt └── build.gradle.kts ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── runConfigurations │ ├── Tests.xml │ ├── Basic_Tests.xml │ ├── Coding_Tests.xml │ ├── Coding_JDK8_Tests.xml │ └── Annotation_Processor_Tests.xml ├── saveactions_settings.xml └── gradle.xml ├── annotations └── build.gradle.kts ├── coding-jdk8 ├── tests-jvm │ ├── data │ │ ├── extended │ │ │ ├── yearData.kt │ │ │ ├── monthDayData.kt │ │ │ ├── yearMonthData.kt │ │ │ ├── zoneIdData.kt │ │ │ ├── zoneOffsetData.kt │ │ │ ├── localDateData.kt │ │ │ ├── localTimeData.kt │ │ │ ├── offsetTimeData.kt │ │ │ ├── localDateTimeData.kt │ │ │ ├── offsetDateTimeData.kt │ │ │ ├── periodData.kt │ │ │ ├── zonedDateTimeData.kt │ │ │ ├── durationData.kt │ │ │ ├── instantData.kt │ │ │ ├── dayOfWeekData.kt │ │ │ └── monthData.kt │ │ └── TestData.kt │ └── utility │ │ ├── Expect.kt │ │ └── Coding.kt ├── build.gradle.kts └── sources-jvm │ ├── codecs │ └── extended │ │ ├── YearJsonCodec.kt │ │ ├── ZoneIdJsonCodec.kt │ │ ├── PeriodJsonCodec.kt │ │ ├── DurationJsonCodec.kt │ │ ├── LocalDateJsonCodec.kt │ │ ├── LocalTimeJsonCodec.kt │ │ ├── ZoneOffsetJsonCodec.kt │ │ ├── InstantJsonCodec.kt │ │ ├── MonthDayJsonCodec.kt │ │ ├── YearMonthJsonCodec.kt │ │ ├── OffsetTimeJsonCodec.kt │ │ ├── LocalDateTimeJsonCodec.kt │ │ ├── ZonedDateTimeJsonCodec.kt │ │ ├── OffsetDateTimeJsonCodec.kt │ │ ├── DayOfWeekJsonCodec.kt │ │ └── MonthJsonCodec.kt │ └── CodingImplementationsJava8.kt ├── basic ├── tests-jvm │ ├── utility │ │ ├── Resources.kt │ │ └── Expect.kt │ ├── JsonReaderTest.kt │ ├── CompanionTest.kt │ ├── dummys │ │ ├── DummyJsonReader.kt │ │ └── DummyJsonWriter.kt │ ├── JsonWriterTest.kt │ ├── StandardWriterRejectTest.kt │ └── JsonTestSuite.kt ├── sources-jvm │ ├── JsonDepth.kt │ ├── JsonToken.kt │ ├── JsonSerializer.kt │ ├── JsonPath.kt │ └── JsonException.kt └── build.gradle.kts ├── .gitignore ├── ktor-client ├── build.gradle.kts └── sources-jvm │ └── FluidJsonSerializer.kt ├── ktor-serialization ├── build.gradle.kts └── sources-jvm │ └── FluidJsonConverter.kt ├── examples ├── sources-jvm │ ├── 0011-ParsingFromReader.kt │ ├── 0020-Serializing.kt │ ├── 0021-SerializingToWriter.kt │ ├── 0014-ParsingAsStream.kt │ ├── 0022-SerializingAsStream.kt │ ├── 0010-Parsing.kt │ ├── 0023-SerializingAsStreamLowLevel.kt │ ├── 0015-ParsingAsStreamLowLevel.kt │ ├── 0001-CodecProvider.kt │ ├── 0012-ParsingLists.kt │ ├── 0013-ParsingMaps.kt │ ├── 0002-CustomProperties.kt │ ├── 0000-Basics.kt │ └── 0030-TypeEncoderCodecs.kt └── build.gradle.kts ├── .github └── workflows │ └── tests.yml └── settings.gradle.kts /annotation-processor/test-cases/.gitignore: -------------------------------------------------------------------------------- 1 | output-actual 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluidsonic/fluid-json/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dependencies/JSONTestSuite"] 2 | path = dependencies/JSONTestSuite 3 | url = https://github.com/nst/JSONTestSuite.git 4 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/Int.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.math.* 4 | 5 | 6 | internal fun Int.toBigDecimal() = 7 | BigDecimal(this) 8 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/Double.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.math.* 4 | 5 | 6 | internal fun Double.toBigDecimal() = 7 | BigDecimal(this) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/DefaultRoot.kt: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.json.* 2 | 3 | 4 | @Json 5 | class DefaultRoot(val value: String) 6 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/classes/Class.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Class(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultObject.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | object DefaultObject 8 | -------------------------------------------------------------------------------- /coding/tests-jvm/model/Universe.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal data class Universe( 5 | val jaegers: Collection, 6 | val kaijus: Collection 7 | ) 8 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/TestCoderContext.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class TestCoderContext : JsonCodingContext 7 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecName/Default.kt: -------------------------------------------------------------------------------- 1 | package json.codecName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Default(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/classes/DataClass.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | data class DataClass(val value: String) 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/classes/InlineClass.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | inline class InlineClass(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/ErrorLogger.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | 4 | internal interface ErrorLogger { 5 | 6 | fun logError(message: String) 7 | } 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/Default.kt: -------------------------------------------------------------------------------- 1 | package json.codecPackageName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Default(val value: String) 8 | -------------------------------------------------------------------------------- /annotations/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (annotations)") { 4 | targets { 5 | jvm() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/classes/Object.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | object Object { 8 | 9 | val value = "" 10 | } 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/DefaultInternal.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultInternal(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/DefaultSingleValue.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultSingleValue(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/DefaultStructured.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultStructured(val value: String) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/AutomaticRoot.kt: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.json.* 2 | 3 | 4 | @Json( 5 | codecPackageName = Json.automatic 6 | ) 7 | class AutomaticRoot(val value: String) 8 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/booleanData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val booleanData: TestData = TestData( 5 | symmetric = mapOf( 6 | true to "true", 7 | false to "false" 8 | ) 9 | ) 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecName/Automatic.kt: -------------------------------------------------------------------------------- 1 | package json.codecName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecName = Json.automatic 8 | ) 9 | class Automatic(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecName/Custom.kt: -------------------------------------------------------------------------------- 1 | package json.codecName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecName = "CustomizedJsonCodec" 8 | ) 9 | class Custom(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticObject.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | object AutomaticObject 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultPrimaryConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultPrimaryConstructor( 8 | val value: String 9 | ) 10 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/yearData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val yearData: TestData = TestData( 7 | symmetric = mapOf( 8 | Year.of(2007) to "2007" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/CustomRoot.kt: -------------------------------------------------------------------------------- 1 | package json.codecPackageName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecPackageName = "" 8 | ) 9 | class CustomRoot(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/Automatic.kt: -------------------------------------------------------------------------------- 1 | package json.codecPackageName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecPackageName = Json.automatic 8 | ) 9 | class Automatic(val value: String) 10 | -------------------------------------------------------------------------------- /coding/sources-jvm/JsonCodingContext.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public interface JsonCodingContext { 5 | 6 | public companion object { 7 | 8 | public val empty: JsonCodingContext = object : JsonCodingContext {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/2/input/codecProvider/StandardContextCodecProvider.kt: -------------------------------------------------------------------------------- 1 | package codecProvider 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json.CodecProvider 7 | interface StandardContextCodecProvider : JsonCodecProvider 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/Internal.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecVisibility = Json.CodecVisibility.internal 8 | ) 9 | class Internal(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/Public.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecVisibility = Json.CodecVisibility.publicRequired 8 | ) 9 | class Public(val value: String) 10 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/monthDayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val monthDayData: TestData = TestData( 7 | symmetric = mapOf( 8 | MonthDay.parse("--12-03") to "\"--12-03\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/charData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val charData: TestData = TestData( 5 | symmetric = mapOf( 6 | 'a' to """"a"""", 7 | 0.toChar() to """"\u0000"""", 8 | 65535.toChar() to "\"\uFFFF\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/TypeResolver.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import javax.lang.model.element.* 4 | 5 | 6 | internal interface TypeResolver { 7 | 8 | fun resolveType(qualifiedName: String): TypeElement? 9 | } 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecPackageName/Custom.kt: -------------------------------------------------------------------------------- 1 | package json.codecPackageName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecPackageName = "json.codecPackageName.customized" 8 | ) 9 | class Custom(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/SingleValue.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.singleValue 8 | ) 9 | class SingleValue(val value: String) 10 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/yearMonthData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val yearMonthData: TestData = TestData( 7 | symmetric = mapOf( 8 | YearMonth.parse("2007-12") to "\"2007-12\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/zoneIdData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val zoneIdData: TestData = TestData( 7 | symmetric = mapOf( 8 | ZoneId.of("Europe/Berlin") to "\"Europe/Berlin\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/zoneOffsetData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val zoneOffsetData: TestData = TestData( 7 | symmetric = mapOf( 8 | ZoneOffset.of("+01:00") to "\"+01:00\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/Structured.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.structured 8 | ) 9 | inline class Structured(val value: String) 10 | -------------------------------------------------------------------------------- /basic/tests-jvm/utility/Resources.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | 4 | internal object Resources { 5 | 6 | fun stream(name: String) = 7 | Resources::class.java.getResourceAsStream("/$name") 8 | ?: throw IllegalArgumentException("Resource not found: $name") 9 | } 10 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/localDateData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val localDateData: TestData = TestData( 7 | symmetric = mapOf( 8 | LocalDate.parse("2007-12-03") to "\"2007-12-03\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/localTimeData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val localTimeData: TestData = TestData( 7 | symmetric = mapOf( 8 | LocalTime.parse("10:15:30") to "\"10:15:30\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/AutomaticPublic.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecVisibility = Json.CodecVisibility.automatic 8 | ) 9 | class AutomaticPublic(val value: String) 10 | -------------------------------------------------------------------------------- /basic/sources-jvm/JsonDepth.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | @JvmInline 5 | public value class JsonDepth(public val value: Int) : Comparable { 6 | 7 | override operator fun compareTo(other: JsonDepth): Int = 8 | value.compareTo(other.value) 9 | } 10 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/intArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val intArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | intArrayOf() to "[]", 7 | intArrayOf(0) to "[0]", 8 | intArrayOf(0, 1) to "[0,1]" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/AutomaticStructured.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.automatic 8 | ) 9 | class AutomaticStructured(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/SingleValueNullable.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.singleValue 8 | ) 9 | class SingleValueNullable(val value: String?) 10 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/byteArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val byteArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | byteArrayOf() to "[]", 7 | byteArrayOf(0) to "[0]", 8 | byteArrayOf(0, 1) to "[0,1]" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/longArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val longArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | longArrayOf() to "[]", 7 | longArrayOf(0) to "[0]", 8 | longArrayOf(0, 1) to "[0,1]" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/AutomaticInternal.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | codecVisibility = Json.CodecVisibility.automatic 8 | ) 9 | internal class AutomaticInternal(val value: String) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticPrimaryConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | class AutomaticPrimaryConstructor( 10 | val value: String 11 | ) 12 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/AutomaticSingleValue.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.automatic 8 | ) 9 | inline class AutomaticSingleValue(val value: String) 10 | -------------------------------------------------------------------------------- /basic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (basic variant)") { 4 | targets { 5 | jvm { 6 | testDependencies { 7 | implementation(kotlin("reflect")) 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/offsetTimeData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val offsetTimeData: TestData = TestData( 7 | symmetric = mapOf( 8 | OffsetTime.parse("10:15:30+01:00") to "\"10:15:30+01:00\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/shortArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val shortArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | shortArrayOf() to "[]", 7 | shortArrayOf(0) to "[0]", 8 | shortArrayOf(0, 1) to "[0,1]" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/StructuredValueGeneric.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.structured 8 | ) 9 | class StructuredValueGeneric(val value: Value) 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/property/serializedName/Default.kt: -------------------------------------------------------------------------------- 1 | package property.serializedName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Default( 8 | @Json.Property 9 | val value1: String 10 | ) { 11 | 12 | @Json.Property 13 | val value2 = "" 14 | } 15 | -------------------------------------------------------------------------------- /coding-jdk8/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (JDK8+ coding extension)") { 4 | targets { 5 | jvm { 6 | dependencies { 7 | api(project(":fluid-json-coding")) 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ByteArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ByteArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: ByteArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/CharArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object CharArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: CharArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/IntArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object IntArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: IntArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/LongArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object LongArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: LongArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/None.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.none 8 | ) 9 | class None( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | } 15 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/FloatArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object FloatArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: FloatArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ShortArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ShortArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: ShortArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/arrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val arrayData: TestData> = TestData( 5 | encodableOnly = mapOf( 6 | emptyArray() to "[]", 7 | arrayOf("one") to """["one"]""", 8 | arrayOf("one", "two") to """["one","two"]""" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/classes/GenericClass.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | data class GenericClass( 8 | val a: A, 9 | val b: B, 10 | val c: C 11 | ) { 12 | 13 | interface Bound 14 | } 15 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/localDateTimeData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val localDateTimeData: TestData = TestData( 7 | symmetric = mapOf( 8 | LocalDateTime.parse("2007-12-03T10:15:30") to "\"2007-12-03T10:15:30\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/DoubleArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object DoubleArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: DoubleArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/BooleanArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object BooleanArrayJsonCodec : AbstractJsonEncoderCodec() { 5 | 6 | override fun JsonEncoder.encode(value: BooleanArray): Unit = 7 | writeList(value) 8 | } 9 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/collectionData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val collectionData: TestData> = TestData( 5 | symmetric = mapOf( 6 | emptyList() to "[]", 7 | listOf("one") to """["one"]""", 8 | listOf("one", "two") to """["one","two"]""" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/booleanArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val booleanArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | booleanArrayOf() to "[]", 7 | booleanArrayOf(true) to "[true]", 8 | booleanArrayOf(true, false) to "[true,false]" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/** 2 | !/.idea/codeStyles/ 3 | !/.idea/codeStyles/** 4 | !/.idea/inspectionProfiles/ 5 | !/.idea/inspectionProfiles/** 6 | !/.idea/runConfigurations/ 7 | !/.idea/runConfigurations/** 8 | !/.idea/gradle.xml 9 | !/.idea/saveactions_settings.xml 10 | .DS_Store 11 | .gradle 12 | build 13 | out 14 | *.iml 15 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/offsetDateTimeData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val offsetDateTimeData: TestData = TestData( 7 | symmetric = mapOf( 8 | OffsetDateTime.parse("2007-12-03T10:15:30+01:00") to "\"2007-12-03T10:15:30+01:00\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/charArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val charArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | charArrayOf() to "[]", 7 | charArrayOf(0.toChar()) to """["\u0000"]""", 8 | charArrayOf(0.toChar(), 'a') to """["\u0000","a"]""" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /ktor-client/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (Ktor Client extension)") { 4 | targets { 5 | jvm { 6 | dependencies { 7 | api(project(":fluid-json-coding")) 8 | api("io.ktor:ktor-client-json:1.6.8") 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/ExecutableElement.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import javax.lang.model.element.* 4 | 5 | 6 | internal val ExecutableElement.jvmMethodSignature 7 | get() = jvmMethodSignature(name = simpleName.toString(), parameterTypes = parameters.map { it.asType() }, returnType = returnType) 8 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/customProperties/DifferentPackageExtensions.kt: -------------------------------------------------------------------------------- 1 | package customProperties.differentPackage 2 | 3 | import io.fluidsonic.json.* 4 | import customProperties.DifferentPackage 5 | 6 | 7 | @Json.CustomProperties 8 | internal fun JsonEncoder.writeCustomProperties3(value: DifferentPackage) = Unit 9 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/PrimaryConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.primaryConstructor 8 | ) 9 | class PrimaryConstructor( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | } 15 | -------------------------------------------------------------------------------- /basic/tests-jvm/JsonReaderTest.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import kotlin.test.* 4 | 5 | 6 | class JsonReaderTest { 7 | 8 | @Test 9 | fun testDefaultReadDelegations() { 10 | val reader = object : DummyJsonReader() { 11 | override fun readDouble() = 10.0 12 | } 13 | 14 | expect(reader.readFloat()).toBe(10.0f) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/periodData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val periodData: TestData = TestData( 7 | symmetric = mapOf( 8 | Period.parse("P1Y2M25D") to "\"P1Y2M25D\"" 9 | ), 10 | decodableOnly = mapOf( 11 | "\"P1Y2M3W4D\"" to Period.parse("P1Y2M3W4D") 12 | ) 13 | ) 14 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/zonedDateTimeData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val zonedDateTimeData: TestData = TestData( 7 | symmetric = mapOf( 8 | ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]") to "\"2007-12-03T10:15:30+01:00[Europe/Paris]\"" 9 | ) 10 | ) 11 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/representation/SingleValueGeneric.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | representation = Json.Representation.singleValue 8 | ) 9 | class SingleValueGeneric(val value: Value) { 10 | 11 | interface Bound 12 | } 13 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/durationData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val durationData: TestData = TestData( 7 | symmetric = mapOf( 8 | Duration.parse("PT51H4M") to "\"PT51H4M\"" 9 | ), 10 | decodableOnly = mapOf( 11 | "\"P2DT3H4M\"" to Duration.parse("P2DT3H4M") 12 | ) 13 | ) 14 | -------------------------------------------------------------------------------- /ktor-serialization/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (Ktor Client extension)") { 4 | targets { 5 | jvm { 6 | dependencies { 7 | api(project(":fluid-json-coding")) 8 | api("io.ktor:ktor-serialization:2.3.1") 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/property/serializedName/Custom.kt: -------------------------------------------------------------------------------- 1 | package property.serializedName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Custom( 8 | @Json.Property(serializedName = "V A L U E 1") 9 | val value1: String 10 | ) { 11 | 12 | @Json.Property(serializedName = "V A L U E 2") 13 | val value2 = "" 14 | } 15 | -------------------------------------------------------------------------------- /coding/tests-jvm/model/Jaeger.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal data class Jaeger( 5 | val height: Double, 6 | val launchDate: YearMonthDay, 7 | val mark: Int, 8 | val name: String, 9 | val origin: String, 10 | val status: Status, 11 | val weight: Double 12 | ) { 13 | 14 | enum class Status { 15 | 16 | destroyed 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /coding/tests-jvm/model/Kaiju.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal data class Kaiju( 5 | val breachDate: YearMonthDay, 6 | val category: Int, 7 | val height: Double, 8 | val name: String, 9 | val origin: String, 10 | val status: Status, 11 | val weight: Double 12 | ) { 13 | 14 | enum class Status { 15 | 16 | deceased 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultSecondaryConstructorPrimaryExcluded.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultSecondaryConstructorPrimaryExcluded @Json.Excluded constructor( 8 | val value: String 9 | ) { 10 | 11 | constructor(value: Int) : this(value.toString()) 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultSecondaryConstructorPrimaryInaccessible.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultSecondaryConstructorPrimaryInaccessible private constructor( 8 | val value: String 9 | ) { 10 | 11 | constructor(value: Int) : this(value.toString()) 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultSecondaryConstructorPrimaryNotPresent.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultSecondaryConstructorPrimaryNotPresent { 8 | 9 | var value = "" 10 | 11 | 12 | constructor(value: Int) { 13 | this.value = value.toString() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/codecVisibility/AutomaticInternalForContainedClass.kt: -------------------------------------------------------------------------------- 1 | package json.codecVisibility 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class AutomaticInternalForContainedClass { 7 | 8 | @Json( 9 | codecVisibility = Json.CodecVisibility.automatic 10 | ) 11 | class ContainedClass(val value: String) 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/property/serializedName/Automatic.kt: -------------------------------------------------------------------------------- 1 | package property.serializedName 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Automatic( 8 | @Json.Property(serializedName = Json.automatic) 9 | val value1: String 10 | ) { 11 | 12 | @Json.Property(serializedName = Json.automatic) 13 | val value2 = "" 14 | } 15 | -------------------------------------------------------------------------------- /coding/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (coding extension)") { 4 | targets { 5 | jvm { 6 | dependencies { 7 | api(project(":fluid-json-annotations")) 8 | api(project(":fluid-json-basic")) 9 | api(kotlin("reflect")) 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/sources-jvm/0011-ParsingFromReader.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | val parser = JsonParser.default 9 | 10 | // You can also let the parser read from a Reader 11 | val value = parser.parseValue(StringReader(""" { "hello": "world", "test": 123 } """)) 12 | println(value) 13 | } 14 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/AnnotationMirror.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import javax.lang.model.element.* 4 | 5 | 6 | internal inline fun AnnotationMirror.getValue(key: String) = 7 | elementValues.entries 8 | .firstOrNull { it.key.simpleName.toString() == key } 9 | ?.value 10 | ?.value 11 | as? T 12 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/Any.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import kotlin.contracts.* 4 | 5 | 6 | internal inline fun T.applyIf(condition: Boolean, block: T.() -> Unit): T { 7 | contract { 8 | callsInPlace(block, InvocationKind.AT_MOST_ONCE) 9 | } 10 | 11 | if (condition) 12 | block() 13 | 14 | return this 15 | } 16 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/MQualifiedTypeName.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import com.squareup.kotlinpoet.* 4 | import io.fluidsonic.meta.* 5 | 6 | 7 | internal fun MQualifiedTypeName.forKotlinPoet(nullable: Boolean = false) = 8 | ClassName(packageName.kotlin, withoutPackage().kotlin) 9 | .copy(nullable = nullable) as ClassName 10 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/DefaultAnnotatedConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DefaultAnnotatedConstructor( 8 | val value: String 9 | ) { 10 | 11 | constructor(value: Int) : this(value.toString()) 12 | 13 | @Json.Constructor 14 | constructor(value: Long) : this(value.toString()) 15 | } 16 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/property/defaultValue/AutomaticGeneric.kt: -------------------------------------------------------------------------------- 1 | package property.defaultValue 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class AutomaticGeneric( 8 | val value1: T, 9 | val value2: T = error(""), 10 | val value3: T?, 11 | val value4: T? = error("") 12 | ) { 13 | 14 | interface SomeInterface 15 | } 16 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/IntJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object IntJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Int = 7 | readInt() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Int): Unit = 11 | writeInt(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/instantData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val instantData: TestData = TestData( 7 | symmetric = mapOf( 8 | Instant.parse("2007-12-03T10:15:30Z") to "\"2007-12-03T10:15:30Z\"" 9 | ), 10 | decodableOnly = mapOf( 11 | "\"2007-12-03T10:15:30.00Z\"" to Instant.parse("2007-12-03T10:15:30.00Z") 12 | ) 13 | ) 14 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ByteJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ByteJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Byte = 7 | readByte() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Byte): Unit = 11 | writeByte(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/CharJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object CharJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Char = 7 | readChar() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Char): Unit = 11 | writeChar(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/LongJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object LongJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Long = 7 | readLong() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Long): Unit = 11 | writeLong(value) 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/AnnotatedConstruct.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import io.fluidsonic.meta.* 4 | import javax.lang.model.* 5 | 6 | 7 | internal fun AnnotatedConstruct.getAnnotationMirror(typeName: MQualifiedTypeName) = 8 | typeName.kotlin.let { jvmTypeName -> 9 | annotationMirrors.firstOrNull { it.annotationType.toString() == jvmTypeName } 10 | } 11 | -------------------------------------------------------------------------------- /basic/tests-jvm/utility/Expect.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import kotlin.test.* 4 | 5 | 6 | @JvmInline 7 | value class Expect(private val actual: Any?) { 8 | 9 | fun toBe(expected: Value) { 10 | assertEquals(expected = expected, actual = actual) 11 | } 12 | } 13 | 14 | 15 | @Suppress("NOTHING_TO_INLINE") 16 | inline fun expect(actual: Value): Expect = 17 | Expect(actual) 18 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticSecondaryConstructorPrimaryExcluded.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | class AutomaticSecondaryConstructorPrimaryExcluded @Json.Excluded constructor( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | } 15 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticSecondaryConstructorPrimaryInaccessible.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | class AutomaticSecondaryConstructorPrimaryInaccessible private constructor( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | } 15 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticSecondaryConstructorPrimaryNotPresent.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | class AutomaticSecondaryConstructorPrimaryNotPresent { 10 | 11 | var value = "" 12 | 13 | 14 | constructor(value: Int) { 15 | this.value = value.toString() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/FloatJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object FloatJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Float = 7 | readFloat() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Float): Unit = 11 | writeFloat(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ShortJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ShortJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Short = 7 | readShort() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Short): Unit = 11 | writeShort(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/Expect.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import kotlin.test.* 4 | 5 | 6 | @JvmInline 7 | value class Expect(private val actual: Any?) { 8 | 9 | fun toBe(expected: Value) { 10 | assertEquals(expected = expected, actual = actual) 11 | } 12 | } 13 | 14 | 15 | @Suppress("NOTHING_TO_INLINE") 16 | inline fun expect(actual: Value): Expect = 17 | Expect(actual) 18 | -------------------------------------------------------------------------------- /examples/sources-jvm/0020-Serializing.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | fun main() { 7 | val serializer = JsonSerializer.default 8 | 9 | // Given a value of a basic Kotlin type (String, Int, List, Map, etc.) you can get a JSON string 10 | val json = serializer.serializeValue(mapOf( 11 | "hello" to "world", 12 | "test" to 123 13 | )) 14 | 15 | println(json) 16 | } 17 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/DoubleJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object DoubleJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Double = 7 | readDouble() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Double): Unit = 11 | writeDouble(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/NumberJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object NumberJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Number = 7 | readNumber() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Number): Unit = 11 | writeNumber(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/StringJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object StringJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): String = 7 | readString() 8 | 9 | 10 | override fun JsonEncoder.encode(value: String): Unit = 11 | writeString(value) 12 | } 13 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/BooleanJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object BooleanJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Boolean = 7 | readBoolean() 8 | 9 | 10 | override fun JsonEncoder.encode(value: Boolean): Unit = 11 | writeBoolean(value) 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AnnotatedConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.annotatedConstructor 8 | ) 9 | class AnnotatedConstructor( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | 15 | @Json.Constructor 16 | constructor(value: Long) : this(value.toString()) 17 | } 18 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/ConstructorParameterOrder.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | // check that finding the right constructor using reflection uses the correct order of parameters 7 | @Json( 8 | encoding = Json.Encoding.none 9 | ) 10 | class ConstructorParameterOrder( 11 | val b: String = "b", 12 | val a: String = "a", 13 | val c: String = "c" 14 | ) 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/decoding/AutomaticAnnotatedConstructor.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json( 7 | decoding = Json.Decoding.automatic 8 | ) 9 | class AutomaticAnnotatedConstructor( 10 | val value: String 11 | ) { 12 | 13 | constructor(value: Int) : this(value.toString()) 14 | 15 | @Json.Constructor 16 | constructor(value: Long) : this(value.toString()) 17 | } 18 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ArrayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ArrayJsonCodec : AbstractJsonEncoderCodec, JsonCodingContext>() { 5 | 6 | override fun JsonEncoder.encode(value: Array): Unit = 7 | writeList(value) 8 | 9 | 10 | public val nonRecursive: JsonEncoderCodec, JsonCodingContext> = NonRecursiveJsonEncoderCodec.create() 11 | } 12 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/StandardCodingParser.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | internal class StandardCodingParser( 5 | private val context: Context, 6 | private val decoderFactory: (source: JsonReader, context: Context) -> JsonDecoder 7 | ) : JsonCodingParser { 8 | 9 | override fun createDecoder(source: JsonReader) = 10 | decoderFactory(source, context) 11 | } 12 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/IterableJsonEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object IterableJsonEncoderCodec : AbstractJsonEncoderCodec, JsonCodingContext>() { 5 | 6 | override fun JsonEncoder.encode(value: Iterable<*>): Unit = 7 | writeList(value) 8 | 9 | 10 | public val nonRecursive: JsonEncoderCodec, JsonCodingContext> = NonRecursiveJsonEncoderCodec.create() 11 | } 12 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/extended/EnumJsonCodecProvider.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | @Suppress("UNCHECKED_CAST") 5 | public class EnumJsonCodecProvider( 6 | public val transformation: EnumJsonTransformation 7 | ) : JsonCodecProvider by JsonCodecProvider.factoryOf>({ enumClass -> 8 | EnumJsonCodec( 9 | enumClass = enumClass, 10 | transformation = transformation 11 | ) as EnumJsonCodec> 12 | }) 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/customProperties/SamePackage.kt: -------------------------------------------------------------------------------- 1 | package customProperties 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class SamePackage(val value: String) { 8 | 9 | @Json.CustomProperties 10 | internal fun JsonEncoder.writeCustomProperties1() = Unit 11 | } 12 | 13 | 14 | @Json.CustomProperties 15 | internal fun JsonEncoder.writeCustomProperties2(value: SamePackage) = Unit 16 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/floatArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val floatArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | floatArrayOf() to "[]", 7 | floatArrayOf(0.0f) to "[0.0]", 8 | floatArrayOf(0.0f, 1.0f) to "[0.0,1.0]" 9 | ), 10 | nonEncodable = setOf( 11 | floatArrayOf(Float.NaN), 12 | floatArrayOf(Float.NEGATIVE_INFINITY), 13 | floatArrayOf(Float.POSITIVE_INFINITY) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /examples/sources-jvm/0021-SerializingToWriter.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | val serializer = JsonSerializer.default 9 | 10 | // You can also let the serializer write to a Writer 11 | val writer = StringWriter() 12 | serializer.serializeValue(mapOf( 13 | "hello" to "world", 14 | "test" to 123 15 | ), destination = writer) 16 | 17 | println(writer.toString()) 18 | } 19 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/doubleArrayData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val doubleArrayData: TestData = TestData( 5 | encodableOnly = mapOf( 6 | doubleArrayOf() to "[]", 7 | doubleArrayOf(0.0) to "[0.0]", 8 | doubleArrayOf(0.0, 1.0) to "[0.0,1.0]" 9 | ), 10 | nonEncodable = setOf( 11 | doubleArrayOf(Double.NaN), 12 | doubleArrayOf(Double.NEGATIVE_INFINITY), 13 | doubleArrayOf(Double.POSITIVE_INFINITY) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/setData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val setData = TestData>( 5 | symmetric = mapOf( 6 | emptySet() to "[]", 7 | setOf(1) to "[1]", 8 | setOf(true, "hey", null) to """[true,"hey",null]""", 9 | setOf(emptyList(), listOf(1)) to "[[],[1]]" 10 | ), 11 | decodableOnly = mapOf( 12 | " [ \t\n\r] " to emptySet(), 13 | "[ [], [ 1 ] ]" to setOf(emptyList(), listOf(1)) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/customProperties/DifferentPackage.kt: -------------------------------------------------------------------------------- 1 | package customProperties 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class DifferentPackage(val value: String) { 8 | 9 | @Json.CustomProperties 10 | internal fun JsonEncoder.writeCustomProperties1() = Unit 11 | } 12 | 13 | 14 | @Json.CustomProperties 15 | internal fun JsonEncoder.writeCustomProperties2(value: DifferentPackage) = Unit 16 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/listData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val listData = TestData>( 5 | symmetric = mapOf( 6 | emptyList() to "[]", 7 | listOf(1) to "[1]", 8 | listOf(true, "hey", null) to """[true,"hey",null]""", 9 | listOf(emptyList(), listOf(1)) to "[[],[1]]" 10 | ), 11 | decodableOnly = mapOf( 12 | " [ \t\n\r] " to emptyList(), 13 | "[ [], [ 1 ] ]" to listOf(emptyList(), listOf(1)) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/encoding/None.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.Serializable 5 | 6 | 7 | @Json( 8 | encoding = Json.Encoding.none 9 | ) 10 | class None( 11 | val value1: String 12 | ) : Serializable by "" { 13 | 14 | val value2 = "" 15 | 16 | private val value3 = "" 17 | 18 | val value4 19 | get() = "" 20 | 21 | val Unit.value5 22 | get() = "" 23 | } 24 | 25 | val None.value6 26 | get() = "" 27 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/TypeNames.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import io.fluidsonic.json.* 4 | import io.fluidsonic.meta.* 5 | 6 | 7 | internal object TypeNames { 8 | 9 | val anyType = MQualifiedTypeName.of(Any::class) 10 | val codecProviderType = MQualifiedTypeName.of(JsonCodecProvider::class) 11 | val codingContext = MQualifiedTypeName.of(JsonCodingContext::class) 12 | val encoder = MQualifiedTypeName.of(JsonEncoder::class) 13 | } 14 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/property/defaultValue/Automatic.kt: -------------------------------------------------------------------------------- 1 | package property.defaultValue 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json 7 | class Automatic( 8 | val value1: String, 9 | val value2: String = "", 10 | val value3: String?, 11 | val value4: String? = "", 12 | val value5: Double, 13 | val value6: Double = 1.2, 14 | val value7: Double?, 15 | val value8: Double? = 1.2, 16 | val value9: List, 17 | val value10: List = emptyList() 18 | ) 19 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/utility/Expect.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import kotlin.test.* 4 | 5 | 6 | @JvmInline 7 | value class Expect(private val actual: Any?) { 8 | 9 | fun notToBeNull() { 10 | assertNotNull(actual) 11 | } 12 | 13 | 14 | fun toBe(expected: Value) { 15 | assertEquals(expected = expected, actual = actual) 16 | } 17 | } 18 | 19 | 20 | @Suppress("NOTHING_TO_INLINE") 21 | inline fun expect(actual: Value): Expect = 22 | Expect(actual) 23 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | jdk: 7 | name: JDK ${{ matrix.java_version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | java_version: [ '17' ] 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions/setup-java@v1 15 | with: 16 | java-version: ${{ matrix.java_version }} 17 | - run: git submodule update --init 18 | - run: ./gradlew --no-daemon jvmTest 19 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/AnyJsonTestDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object AnyJsonTestDecoderCodec : AbstractJsonDecoderCodec( 7 | additionalProviders = listOf(BooleanJsonCodec, ListJsonDecoderCodec, MapJsonCodec, NumberJsonCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType) = 11 | AnyJsonDecoderCodec.run { decode(valueType) } 12 | } 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/customProperties/CustomContext.kt: -------------------------------------------------------------------------------- 1 | package customProperties 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.* 5 | 6 | 7 | @Json 8 | class CustomContext(val value: String) { 9 | 10 | @Json.CustomProperties 11 | internal fun JsonEncoder.writeCustomProperties1() = Unit 12 | } 13 | 14 | 15 | @Json.CustomProperties 16 | internal fun JsonEncoder.writeCustomProperties2(value: CustomContext) = Unit 17 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/dayOfWeekData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val dayOfWeekData: TestData = TestData( 7 | symmetric = mapOf( 8 | DayOfWeek.MONDAY to "\"monday\"", 9 | DayOfWeek.TUESDAY to "\"tuesday\"", 10 | DayOfWeek.WEDNESDAY to "\"wednesday\"", 11 | DayOfWeek.THURSDAY to "\"thursday\"", 12 | DayOfWeek.FRIDAY to "\"friday\"", 13 | DayOfWeek.SATURDAY to "\"saturday\"", 14 | DayOfWeek.SUNDAY to "\"sunday\"" 15 | ) 16 | ) 17 | -------------------------------------------------------------------------------- /examples/sources-jvm/0014-ParsingAsStream.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | // You can read values directly from a stream without having to parse all at once 9 | val input = StringReader("""{ "data": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }""") 10 | JsonReader.build(input).use { reader -> 11 | reader.readFromMapByElementValue { key -> 12 | println(key) 13 | 14 | readFromListByElement { 15 | println(readInt()) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "examples") { 4 | withoutPublishing() 5 | 6 | language { 7 | withoutExplicitApi() 8 | } 9 | 10 | targets { 11 | jvm { 12 | withJava() 13 | 14 | dependencies { 15 | implementation(project(":fluid-json-coding-jdk8")) 16 | implementation(project(":fluid-json-annotations")) 17 | 18 | kapt(project(":fluid-json-annotation-processor")) 19 | } 20 | } 21 | } 22 | } 23 | 24 | kotlin { 25 | jvmToolchain(8) 26 | } 27 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/ContextCheckingTestEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class ContextCheckingTestEncoderCodec( 7 | private val expectedContext: Context 8 | ) : JsonEncoderCodec { 9 | 10 | override fun JsonEncoder.encode(value: String) { 11 | expect(context).toBe(expectedContext) 12 | 13 | StringJsonCodec.run { encode(value) } 14 | } 15 | 16 | 17 | override val encodableClass = String::class 18 | } 19 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/sequenceData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val sequenceData = TestData>( 5 | symmetric = mapOf( 6 | emptyEquatableSequence() to "[]", 7 | equatableSequenceOf(1) to "[1]", 8 | equatableSequenceOf(true, "hey", null) to """[true,"hey",null]""", 9 | equatableSequenceOf(emptyList(), listOf(1)) to "[[],[1]]" 10 | ), 11 | decodableOnly = mapOf( 12 | " [ \t\n\r] " to emptyEquatableSequence(), 13 | "[ [], [ 1 ] ]" to equatableSequenceOf(emptyList(), listOf(1)) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/StandardCodingSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | internal class StandardCodingSerializer( 5 | private val context: Context, 6 | private val encoderFactory: (destination: JsonWriter, context: Context) -> JsonEncoder 7 | ) : JsonCodingSerializer { 8 | 9 | override fun serializeValue(value: Any?, destination: JsonWriter, withTermination: Boolean) { 10 | encoderFactory(destination, context) 11 | .withTermination(withTermination) { writeValueOrNull(value) } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/sources-jvm/0022-SerializingAsStream.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | // You can write values directly to a stream without having to build the temporary map or list 9 | val output = StringWriter() 10 | JsonWriter.build(output).use { writer -> 11 | writer.writeIntoMap { 12 | writeMapElement("data") { 13 | writeIntoList { 14 | for (value in 0 .. 10) { 15 | writer.writeInt(value) 16 | } 17 | } 18 | } 19 | } 20 | } 21 | 22 | println(output.toString()) 23 | } 24 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/Coding.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal fun JsonDecoderCodec.parse(source: String, type: JsonCodingType) = 7 | JsonCodingParser.builder() 8 | .decodingWith(this, base = null) 9 | .build() 10 | .parseValueOfType(source, type) 11 | 12 | 13 | internal fun JsonEncoderCodec.serialize(value: Value) = 14 | JsonCodingSerializer.builder() 15 | .encodingWith(this, base = null) 16 | .build() 17 | .serializeValue(value) 18 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/utility/Coding.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal fun JsonDecoderCodec.parse(source: String, type: JsonCodingType) = 7 | JsonCodingParser.builder() 8 | .decodingWith(this, base = null) 9 | .build() 10 | .parseValueOfType(source, type) 11 | 12 | 13 | internal fun JsonEncoderCodec.serialize(value: Value) = 14 | JsonCodingSerializer.builder() 15 | .encodingWith(this, base = null) 16 | .build() 17 | .serializeValue(value) 18 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/ContextCheckingTestDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class ContextCheckingTestDecoderCodec( 7 | private val expectedContext: Context 8 | ) : JsonDecoderCodec { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType): String { 11 | expect(context).toBe(expectedContext) 12 | 13 | return StringJsonCodec.run { decode(valueType) } 14 | } 15 | 16 | 17 | override val decodableType = jsonCodingType() 18 | } 19 | -------------------------------------------------------------------------------- /examples/sources-jvm/0010-Parsing.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | fun main() { 7 | val parser = JsonParser.default 8 | 9 | // Given a JSON string you get a value of basic Kotlin types (String, Int, List, Map, etc.) 10 | val value1 = parser.parseValue(""" { "hello": "world", "test": 123 } """) 11 | println(value1) 12 | 13 | // Many functions have `…OrNull` versions which allow a `null` value to be returned instead of causing a 14 | // `JsonException` in such cases. 15 | val value2 = parser.parseValueOrNull("null") 16 | println(value2) 17 | } 18 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/YearMonthDayCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object YearMonthDayCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType) = 9 | readString().let { raw -> 10 | YearMonthDay.parse(raw) ?: invalidValueError("expected date in format YYYY-MM-DD, got '$raw'") 11 | } 12 | 13 | 14 | override fun JsonEncoder.encode(value: YearMonthDay) { 15 | writeString(value.toString()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /coding/sources-jvm/CodingImplementationsJava7.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.dynamic 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal open class CodingImplementationsJava7 : CodingImplementationsJava { 7 | 8 | override fun extendedCodecProviders() = 9 | super.extendedCodecProviders() + listOf( 10 | // from specific to unspecific 11 | CharRangeJsonCodec, 12 | IntRangeJsonCodec, 13 | LongRangeJsonCodec, 14 | ClosedRangeJsonCodec, 15 | EnumJsonCodecProvider( 16 | transformation = EnumJsonTransformation.ToString(case = EnumJsonTransformation.Case.lowerCamelCase) 17 | ) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /examples/sources-jvm/0023-SerializingAsStreamLowLevel.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | // You can also avoid the higher-order functions for stream serializing and write token by token 9 | val output = StringWriter() 10 | JsonWriter.build(output).use { writer -> 11 | writer.apply { 12 | writeMapStart() 13 | writeMapKey("data") 14 | 15 | writeListStart() 16 | for (value in 0 .. 10) { 17 | writeInt(value) 18 | } 19 | writeListEnd() 20 | 21 | writeMapEnd() 22 | } 23 | } 24 | 25 | println(output.toString()) 26 | } 27 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/encoding/Default.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.Serializable 5 | 6 | 7 | @Json 8 | class Default( 9 | val value1: String 10 | ) : Serializable by "" { 11 | 12 | val value2 = "" 13 | 14 | @Json.Excluded 15 | val value3 = "" 16 | 17 | private val value4 = "" 18 | 19 | val value5 20 | get() = "" 21 | 22 | @Json.Property 23 | val value6 24 | get() = "" 25 | 26 | val Unit.value7 27 | get() = "" 28 | } 29 | 30 | val Default.value8 31 | get() = "" 32 | 33 | @Json.Property 34 | val Default.value9 35 | get() = "" 36 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/YearJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object YearJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Year = 9 | readInt().let { raw -> 10 | try { 11 | Year.of(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("year in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: Year): Unit = 20 | writeInt(value.value) 21 | } 22 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/SetJsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object SetJsonDecoderCodec : AbstractJsonDecoderCodec, JsonCodingContext>() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType>): Set<*> { 7 | val elementType = valueType.arguments.single() 8 | 9 | return mutableSetOf().also { set -> 10 | readFromListByElement { 11 | set += readValueOfTypeOrNull(elementType) 12 | } 13 | } 14 | } 15 | 16 | 17 | public val nonRecursive: JsonDecoderCodec, JsonCodingContext> = NonRecursiveJsonDecoderCodec.create() 18 | } 19 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/extended/monthData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.time.* 4 | 5 | 6 | internal val monthData: TestData = TestData( 7 | symmetric = mapOf( 8 | Month.JANUARY to "\"january\"", 9 | Month.FEBRUARY to "\"february\"", 10 | Month.MARCH to "\"march\"", 11 | Month.APRIL to "\"april\"", 12 | Month.MAY to "\"may\"", 13 | Month.JUNE to "\"june\"", 14 | Month.JULY to "\"july\"", 15 | Month.AUGUST to "\"august\"", 16 | Month.SEPTEMBER to "\"september\"", 17 | Month.OCTOBER to "\"october\"", 18 | Month.NOVEMBER to "\"november\"", 19 | Month.DECEMBER to "\"december\"" 20 | ) 21 | ) 22 | -------------------------------------------------------------------------------- /examples/sources-jvm/0015-ParsingAsStreamLowLevel.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | 6 | 7 | fun main() { 8 | // You can also avoid the higher-order functions for stream parsing and read token by token 9 | val input = StringReader("""{ "data": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }""") 10 | JsonReader.build(input).use { reader -> 11 | reader.apply { 12 | readMapStart() 13 | println(readMapKey()) 14 | 15 | readListStart() 16 | while (nextToken != JsonToken.listEnd) { 17 | println(readInt()) 18 | } 19 | readListEnd() 20 | 21 | readMapEnd() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/ZoneIdJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object ZoneIdJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): ZoneId = 9 | readString().let { raw -> 10 | try { 11 | ZoneId.of(raw) 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("IANA time zone name expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: ZoneId): Unit = 20 | writeString(value.id) 21 | } 22 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/CollectionJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object CollectionJsonCodec : AbstractJsonCodec, JsonCodingContext>() { 5 | 6 | @Suppress("UNCHECKED_CAST") 7 | override fun JsonDecoder.decode(valueType: JsonCodingType>): Collection<*> = 8 | ListJsonDecoderCodec.run { decode(valueType as JsonCodingType>) } 9 | 10 | 11 | override fun JsonEncoder.encode(value: Collection<*>): Unit = 12 | writeList(value) 13 | 14 | 15 | public val nonRecursive: JsonCodec, JsonCodingContext> = NonRecursiveJsonCodec.create() 16 | } 17 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/PeriodJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object PeriodJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Period = 9 | readString().let { raw -> 10 | try { 11 | Period.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("period in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: Period): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /.idea/saveactions_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/2/output-expected/codecProvider/GeneratedStandardContextCodecProvider.kt: -------------------------------------------------------------------------------- 1 | package codecProvider 2 | 3 | import io.fluidsonic.json.JsonCodecProvider 4 | import io.fluidsonic.json.JsonCodingContext 5 | import kotlin.Suppress 6 | import kotlin.reflect.KClass 7 | 8 | private object GeneratedStandardContextCodecProvider : StandardContextCodecProvider, 9 | JsonCodecProvider by JsonCodecProvider() 10 | 11 | @Suppress("UNUSED_PARAMETER") 12 | public 13 | fun JsonCodecProvider.Companion.generated(interfaceClass: KClass): 14 | StandardContextCodecProvider = GeneratedStandardContextCodecProvider 15 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/ListJsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object ListJsonDecoderCodec : AbstractJsonDecoderCodec, JsonCodingContext>() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType>): List<*> { 7 | val elementType = valueType.arguments.single() 8 | 9 | return readListByElement { 10 | // No readValueOfTypeOrNull() to reduce stack size for deep recursive structures. 11 | readOrNull { readValueOfType(elementType) } 12 | } 13 | } 14 | 15 | 16 | public val nonRecursive: JsonDecoderCodec, JsonCodingContext> = NonRecursiveJsonDecoderCodec.create() 17 | } 18 | -------------------------------------------------------------------------------- /coding/tests-jvm/CompanionTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class CompanionTest { 8 | 9 | @Test 10 | fun testInitialization() { 11 | // just make sure all companions initialize properly, and we get more code coverage :) 12 | listOf( 13 | JsonCodingType.Companion, 14 | JsonCodec.Companion, 15 | JsonCodecProvider.Companion, 16 | JsonCodingContext.Companion, 17 | JsonDecoder.Companion, 18 | JsonDecoderCodec.Companion, 19 | JsonEncoder.Companion, 20 | JsonEncoderCodec.Companion, 21 | JsonCodingParser.Companion, 22 | JsonCodingSerializer.Companion 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/plainData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val anyData = TestData.of( 5 | complexData, 6 | iterableData, 7 | listData, 8 | mapData, 9 | numberData, 10 | stringData, 11 | TestData.ofEncodable( 12 | floatData, 13 | booleanArrayData, 14 | booleanData, 15 | byteArrayData, 16 | byteData, 17 | doubleArrayData, 18 | doubleData, 19 | floatArrayData, 20 | floatData, 21 | intArrayData, 22 | intData, 23 | listData, 24 | longArrayData, 25 | longData, 26 | sequenceData, 27 | shortArrayData 28 | ), 29 | TestData( 30 | nonEncodable = setOf( 31 | mapOf("a" to "b").entries.first() 32 | ) 33 | ) 34 | ) 35 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "fluid-json" 2 | 3 | include("annotation-processor", "annotations", "basic", "coding", "coding-jdk8", "examples", "ktor-client", "ktor-serialization") 4 | 5 | project(":annotation-processor").name = "fluid-json-annotation-processor" 6 | project(":annotations").name = "fluid-json-annotations" 7 | project(":basic").name = "fluid-json-basic" 8 | project(":coding").name = "fluid-json-coding" 9 | project(":coding-jdk8").name = "fluid-json-coding-jdk8" 10 | project(":examples").name = "fluid-json-examples" 11 | project(":ktor-client").name = "fluid-json-ktor-client" 12 | project(":ktor-serialization").name = "fluid-json-ktor-serialization" 13 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/encoding/Automatic.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.Serializable 5 | 6 | 7 | @Json( 8 | encoding = Json.Encoding.automatic 9 | ) 10 | class Automatic( 11 | val value1: String 12 | ) : Serializable by "" { 13 | 14 | val value2 = "" 15 | 16 | @Json.Excluded 17 | val value3 = "" 18 | 19 | private val value4 = "" 20 | 21 | val value5 22 | get() = "" 23 | 24 | @Json.Property 25 | val value6 26 | get() = "" 27 | 28 | val Unit.value7 29 | get() = "" 30 | } 31 | 32 | val Automatic.value8 33 | get() = "" 34 | 35 | @Json.Property 36 | val Automatic.value9 37 | get() = "" 38 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/DurationJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object DurationJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Duration = 9 | readString().let { raw -> 10 | try { 11 | Duration.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("duration in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: Duration): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/LocalDateJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object LocalDateJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): LocalDate = 9 | readString().let { raw -> 10 | try { 11 | LocalDate.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: LocalDate): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/LocalTimeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object LocalTimeJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): LocalTime = 9 | readString().let { raw -> 10 | try { 11 | LocalTime.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("time in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: LocalTime): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/ZoneOffsetJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object ZoneOffsetJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): ZoneOffset = 9 | readString().let { raw -> 10 | try { 11 | ZoneOffset.of(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("time offset in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: ZoneOffset): Unit = 20 | writeString(value.id) 21 | } 22 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/Element.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import javax.lang.model.element.* 4 | 5 | 6 | internal val Element.debugName: String 7 | get() { 8 | val components = mutableListOf() 9 | 10 | var element: Element? = this 11 | while (element != null) { 12 | components += when (element) { 13 | is ExecutableElement -> "${element.simpleName}()" 14 | is PackageElement -> element.qualifiedName.toString() 15 | else -> element.simpleName.toString() 16 | } 17 | 18 | element = element.enclosingElement 19 | } 20 | 21 | components.reverse() 22 | 23 | return components.joinToString(".") 24 | } 25 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/InstantJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object InstantJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Instant = 9 | readString().let { raw -> 10 | try { 11 | Instant.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date, time and time zone in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: Instant): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/MonthDayJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object MonthDayJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): MonthDay = 9 | readString().let { raw -> 10 | try { 11 | MonthDay.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date without year in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: MonthDay): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding/sources-jvm/CodingImplementationsJava.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.dynamic 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal interface CodingImplementationsJava { 7 | 8 | fun extendedCodecProviders(): List> = 9 | emptyList() 10 | } 11 | 12 | 13 | internal val codingImplementationsJava: CodingImplementationsJava by lazy { 14 | try { 15 | return@lazy Class.forName("io.fluidsonic.json.dynamic.CodingImplementationsJava8").getDeclaredConstructor().newInstance() 16 | as CodingImplementationsJava 17 | } 18 | catch (e: Exception) { 19 | // non-existent or severely broken 20 | } 21 | 22 | CodingImplementationsJava7() 23 | } 24 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/YearMonthJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object YearMonthJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): YearMonth = 9 | readString().let { raw -> 10 | try { 11 | YearMonth.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date without day in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: YearMonth): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/NonRecursiveJsonEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | 5 | 6 | internal class NonRecursiveJsonEncoderCodec( 7 | override val encodableClass: KClass 8 | ) : JsonEncoderCodec { 9 | 10 | override fun JsonEncoder.encode(value: Value) { 11 | JsonSerializer.default.serializeValue(value, destination = this, withTermination = false) 12 | } 13 | 14 | 15 | companion object { 16 | 17 | inline fun create(): JsonEncoderCodec = 18 | NonRecursiveJsonEncoderCodec(encodableClass = Value::class) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/encoding/AllProperties.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.Serializable 5 | 6 | 7 | @Json( 8 | encoding = Json.Encoding.allProperties 9 | ) 10 | class AllProperties( 11 | val value1: String 12 | ) : Serializable by "" { 13 | 14 | val value2 = "" 15 | 16 | @Json.Excluded 17 | val value3 = "" 18 | 19 | private val value4 = "" 20 | 21 | val value5 22 | get() = "" 23 | 24 | @Json.Property 25 | val value6 26 | get() = "" 27 | 28 | val Unit.value7 29 | get() = "" 30 | } 31 | 32 | val AllProperties.value8 33 | get() = "" 34 | 35 | @Json.Property 36 | val AllProperties.value9 37 | get() = "" 38 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/SequenceJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object SequenceJsonCodec : AbstractJsonCodec, JsonCodingContext>() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType>): Sequence<*> { 7 | val elementType = valueType.arguments.single() 8 | 9 | return readListByElement { 10 | readValueOfTypeOrNull(elementType) 11 | }.asSequence() 12 | } 13 | 14 | 15 | override fun JsonEncoder.encode(value: Sequence<*>): Unit = 16 | writeList(value) 17 | 18 | 19 | public val nonRecursive: JsonCodec, JsonCodingContext> = NonRecursiveJsonCodec.create() 20 | } 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Basic_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Coding_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/OffsetTimeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object OffsetTimeJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): OffsetTime = 9 | readString().let { raw -> 10 | try { 11 | OffsetTime.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("time and time zone in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: OffsetTime): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/LocalDateTimeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object LocalDateTimeJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): LocalDateTime = 9 | readString().let { raw -> 10 | try { 11 | LocalDateTime.parse(raw) 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date and time in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: LocalDateTime): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Coding_JDK8_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /basic/tests-jvm/CompanionTest.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class CompanionTest { 8 | 9 | @Test 10 | fun testInitialization() { 11 | // just make sure all companions initialize properly, and we get more code coverage :) 12 | listOf( 13 | JsonException.Companion, 14 | JsonReader.Companion, 15 | JsonToken.Companion, 16 | JsonWriter.Companion 17 | ) 18 | 19 | // trigger initialization of Character.* objects to make code coverage happy 20 | JsonCharacter::class.nestedClasses.forEach { 21 | try { 22 | it.objectInstance 23 | } 24 | catch (e: IllegalAccessException) { 25 | // ignore 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/AnyJsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object AnyJsonDecoderCodec : AbstractJsonDecoderCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): Any = 7 | when (nextToken) { 8 | JsonToken.booleanValue -> readValueOfType() 9 | JsonToken.listStart -> readValueOfType>() 10 | JsonToken.mapKey -> readValueOfType() 11 | JsonToken.mapStart -> readValueOfType>() 12 | JsonToken.numberValue -> readValueOfType() 13 | JsonToken.stringValue -> readValueOfType() 14 | else -> invalidValueError("unexpected 'null'") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/ArrayJsonTestEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object ArrayJsonTestEncoderCodec : AbstractJsonEncoderCodec, JsonCodingContext>( 7 | additionalProviders = listOf(StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonEncoder.encode(value: Array<*>) = 11 | ArrayJsonCodec.run { encode(value) } 12 | 13 | 14 | object NonRecursive : AbstractJsonEncoderCodec, JsonCodingContext>( 15 | additionalProviders = listOf(StringJsonCodec) 16 | ) { 17 | 18 | override fun JsonEncoder.encode(value: Array<*>) = 19 | ArrayJsonCodec.nonRecursive.run { encode(value) } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/json/encoding/AnnotatedProperties.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.Serializable 5 | 6 | 7 | @Json( 8 | encoding = Json.Encoding.annotatedProperties 9 | ) 10 | class AnnotatedProperties( 11 | val value1: String 12 | ) : Serializable by "" { 13 | 14 | val value2 = "" 15 | 16 | @Json.Property 17 | val value3 = "" 18 | 19 | private val value4 = "" 20 | 21 | val value5 22 | get() = "" 23 | 24 | @Json.Property 25 | val value6 26 | get() = "" 27 | 28 | val Unit.value7 29 | get() = "" 30 | } 31 | 32 | val AnnotatedProperties.value8 33 | get() = "" 34 | 35 | @Json.Property 36 | val AnnotatedProperties.value9 37 | get() = "" 38 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/ZonedDateTimeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object ZonedDateTimeJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): ZonedDateTime = 9 | readString().let { raw -> 10 | try { 11 | ZonedDateTime.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date, time and time zone in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: ZonedDateTime): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/MapJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object MapJsonCodec : AbstractJsonCodec, JsonCodingContext>() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType>): Map<*, *> { 7 | val (elementKeyType, elementValueType) = valueType.arguments 8 | 9 | return readMapByElement { 10 | readValueOfTypeOrNull(elementKeyType) to readValueOfTypeOrNull(elementValueType) 11 | } 12 | } 13 | 14 | 15 | override fun JsonEncoder.encode(value: Map<*, *>): Unit = 16 | writeMap(value) 17 | 18 | 19 | public val nonRecursive: JsonCodec, JsonCodingContext> = NonRecursiveJsonCodec.create() 20 | } 21 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/OffsetDateTimeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object OffsetDateTimeJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): OffsetDateTime = 9 | readString().let { raw -> 10 | try { 11 | OffsetDateTime.parse(raw)!! 12 | } 13 | catch (e: DateTimeException) { 14 | invalidValueError("date, time and time zone in ISO-8601 format expected, got '$raw'") 15 | } 16 | } 17 | 18 | 19 | override fun JsonEncoder.encode(value: OffsetDateTime): Unit = 20 | writeString(value.toString()) 21 | } 22 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/Sequence.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal fun emptyEquatableSequence(): Sequence = 5 | EquatableSequence(emptySequence()) 6 | 7 | 8 | internal fun equatableSequenceOf(vararg elements: T): Sequence = 9 | EquatableSequence(elements.asSequence()) 10 | 11 | 12 | @Suppress("EqualsOrHashCode") 13 | internal class EquatableSequence(val source: Sequence) : Sequence by source { 14 | 15 | // careful, this isn't reflexive 16 | override fun equals(other: Any?): Boolean { 17 | if (other === this) { 18 | return true 19 | } 20 | if (other !is Sequence<*>) { 21 | return false 22 | } 23 | 24 | return toList() == other.toList() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /basic/sources-jvm/JsonToken.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public enum class JsonToken { 5 | 6 | booleanValue, 7 | listEnd, 8 | listStart, 9 | nullValue, 10 | mapEnd, 11 | mapKey, 12 | mapStart, 13 | numberValue, 14 | stringValue; 15 | 16 | 17 | override fun toString(): String = 18 | when (this) { 19 | booleanValue -> "boolean" 20 | listEnd -> "list end" 21 | listStart -> "list" 22 | nullValue -> "null" 23 | mapEnd -> "map end" 24 | mapKey -> "map key" 25 | mapStart -> "map" 26 | numberValue -> "number" 27 | stringValue -> "string" 28 | } 29 | 30 | 31 | public companion object 32 | } 33 | 34 | 35 | public fun JsonToken?.toString(): String = 36 | this?.toString() ?: "end of data" 37 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/mapData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val mapData = TestData>( 5 | symmetric = mapOf( 6 | emptyMap() to "{}", 7 | mapOf("key" to 1) to """{"key":1}""", 8 | mapOf( 9 | "key0" to true, 10 | "key1" to "hey", 11 | "key2" to null 12 | ) to """{"key0":true,"key1":"hey","key2":null}""", 13 | mapOf( 14 | "key0" to emptyMap(), 15 | "key1" to mapOf("key" to 1) 16 | ) to """{"key0":{},"key1":{"key":1}}""" 17 | ), 18 | decodableOnly = mapOf( 19 | " { \t\n\r} " to emptyMap(), 20 | """{ "key0": true, "key1" :"hey", "key2" : null }""" to mapOf( 21 | "key0" to true, 22 | "key1" to "hey", 23 | "key2" to null 24 | ) 25 | ) 26 | ) 27 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/mapDataWithNonStringKeys.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val mapDataWithNonStringKeys = TestData>( 5 | symmetric = mapOf( 6 | emptyMap() to "{}", 7 | mapOf(YearMonthDay(2017, 10, 29) to 1) to """{"2017-10-29":1}""", 8 | mapOf( 9 | YearMonthDay(2017, 10, 27) to true, 10 | YearMonthDay(2017, 10, 28) to "hey", 11 | YearMonthDay(2017, 10, 29) to null 12 | ) to """{"2017-10-27":true,"2017-10-28":"hey","2017-10-29":null}""", 13 | mapOf( 14 | YearMonthDay(2017, 10, 27) to emptyMap(), 15 | YearMonthDay(2017, 10, 28) to mapOf("2017-10-29" to 1) 16 | ) to """{"2017-10-27":{},"2017-10-28":{"2017-10-29":1}}""" 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/ContextCheckingTestCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class ContextCheckingTestCodec( 7 | private val expectedContext: Context 8 | ) : JsonCodec { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType): String { 11 | expect(context).toBe(expectedContext) 12 | 13 | return StringJsonCodec.run { decode(valueType) } 14 | } 15 | 16 | 17 | override fun JsonEncoder.encode(value: String) { 18 | expect(context).toBe(expectedContext) 19 | 20 | StringJsonCodec.run { encode(value) } 21 | } 22 | 23 | 24 | override val decodableType = jsonCodingType() 25 | } 26 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Annotation_Processor_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /coding/tests-jvm/codecs/EnumJsonCodecProviderTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class EnumJsonCodecProviderTest { 9 | 10 | @Test 11 | fun test() { 12 | val provider = EnumJsonCodecProvider(transformation = EnumJsonTransformation.Ordinal) 13 | val serializer = JsonCodingSerializer.builder().encodingWith(provider).build() 14 | 15 | for (enumClass in listOf(Example1::class, Example2::class)) { 16 | expect(serializer.serializeValue(enumClass.java.enumConstants.first())).toBe("0") 17 | } 18 | } 19 | 20 | 21 | private enum class Example1 { 22 | hello1 23 | } 24 | 25 | 26 | private enum class Example2 { 27 | hello2 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/bigDecimalData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import java.math.* 4 | 5 | 6 | internal val bigDecimalData: TestData = TestData( 7 | encodableOnly = mapOf( 8 | (-1E200).toBigDecimal() to "-1.0E200", 9 | (-100.999).toBigDecimal() to "-100.999", 10 | (-100.001).toBigDecimal() to "-100.001", 11 | (-1).toBigDecimal() to "-1.0", 12 | (-1E-200).toBigDecimal() to "-1.0E-200", 13 | (-0.0).toBigDecimal() to "-0.0", 14 | 0.toBigDecimal() to "0.0", 15 | 0.0.toBigDecimal() to "0.0", 16 | 1E-200.toBigDecimal() to "1.0E-200", 17 | 1.toBigDecimal() to "1.0", 18 | 100.001.toBigDecimal() to "100.001", 19 | 100.999.toBigDecimal() to "100.999", 20 | 1E200.toBigDecimal() to "1.0E200" 21 | ) 22 | ) 23 | -------------------------------------------------------------------------------- /basic/sources-jvm/JsonSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.io.* 4 | 5 | 6 | public interface JsonSerializer { 7 | 8 | public fun serializeValue(value: Any?, destination: JsonWriter, withTermination: Boolean = true) 9 | 10 | 11 | public companion object { 12 | 13 | public val default: JsonSerializer get() = StandardSerializer 14 | } 15 | } 16 | 17 | 18 | public fun JsonSerializer.serializeValue(value: Any?): String = 19 | StringWriter().apply { serializeValue(value, destination = this) }.toString() 20 | 21 | 22 | public fun JsonSerializer.serializeValue(value: Any?, destination: Writer, withTermination: Boolean = true) { 23 | serializeValue(value, destination = JsonWriter.build(destination), withTermination = withTermination) 24 | } 25 | -------------------------------------------------------------------------------- /examples/sources-jvm/0001-CodecProvider.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | // Note that IntelliJ IDEA still only has limited supported for annotation processing with kapt, so enable this setting first: 6 | // Preferences > Build, Execution, Deployment > Build Tools > Gradle > Runner > Delegate IDE build/run actions to gradle 7 | 8 | // MyCodecProvider will be generated automatically and provides all JsonCodecs created by annotation processing in the same module. 9 | // A custom context type can be used to provide additional information during encoding. 10 | 11 | @Json.CodecProvider 12 | interface MyCodecProvider : JsonCodecProvider 13 | 14 | 15 | data class MyContext( 16 | val authenticatedUserId: String? = null 17 | ) : JsonCodingContext 18 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/generation/GenerationPhase.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import java.io.* 4 | 5 | 6 | internal class GenerationPhase( 7 | private val outputDirectory: File, 8 | private val processingResult: ProcessingResult 9 | ) { 10 | 11 | private var isGenerated = false 12 | 13 | 14 | fun generate() { 15 | check(!isGenerated) { "can only generate once" } 16 | isGenerated = true 17 | 18 | CodecGenerator(outputDirectory = outputDirectory).run { 19 | processingResult.codecs.forEach(this::generate) 20 | } 21 | 22 | processingResult.codecProvider?.let { codecProvider -> 23 | CodecProviderGenerator(outputDirectory = outputDirectory) 24 | .generate(codecProvider, codecNames = processingResult.codecs.map { it.name }) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /coding/tests-jvm/AbstractJsonDecoderCodecTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class AbstractJsonDecoderCodecTest { 8 | 9 | @Test 10 | fun testEncoderCodecForClassReturnsNested() { 11 | expect(OuterDecoderCodec.encoderCodecForClass(Unit::class)).toBe(InnerEncoderCodec) 12 | } 13 | 14 | 15 | private object InnerEncoderCodec : AbstractJsonEncoderCodec() { 16 | 17 | override fun JsonEncoder.encode(value: Unit) {} 18 | } 19 | 20 | 21 | private object OuterDecoderCodec : AbstractJsonDecoderCodec( 22 | additionalProviders = listOf(InnerEncoderCodec) 23 | ) { 24 | 25 | override fun JsonDecoder.decode(valueType: JsonCodingType) = 26 | "" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/iterableData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | @Suppress("RedundantAsSequence") 5 | internal val iterableData = TestData>( 6 | symmetric = mapOf( 7 | emptyList() to "[]", 8 | listOf(1) to "[1]", 9 | listOf(true, "hey", null) to """[true,"hey",null]""", 10 | listOf(emptyList(), listOf(1)) to "[[],[1]]" 11 | ), 12 | decodableOnly = mapOf( 13 | " [ \t\n\r] " to emptyList(), 14 | "[ [], [ 1 ] ]" to listOf(emptyList(), listOf(1)) 15 | ), 16 | encodableOnly = mapOf( 17 | emptyList().asSequence().asIterable() to "[]", 18 | listOf(1).asSequence().asIterable() to "[1]", 19 | listOf(true, "hey", null).asSequence().asIterable() to """[true,"hey",null]""", 20 | listOf(emptyList(), listOf(1)).asSequence().asIterable() to "[[],[1]]" 21 | ) 22 | ) 23 | -------------------------------------------------------------------------------- /coding/tests-jvm/AbstractJsonEncoderCodecTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class AbstractJsonEncoderCodecTest { 8 | 9 | @Test 10 | fun testDecoderCodecForClassReturnsNested() { 11 | expect(OuterEncoderCodec.decoderCodecForType()).toBe(InnerDecoderCodec) 12 | } 13 | 14 | 15 | private object InnerDecoderCodec : AbstractJsonDecoderCodec() { 16 | 17 | override fun JsonDecoder.decode(valueType: JsonCodingType) = 18 | Unit 19 | } 20 | 21 | 22 | private object OuterEncoderCodec : AbstractJsonEncoderCodec( 23 | additionalProviders = listOf(InnerDecoderCodec) 24 | ) { 25 | 26 | override fun JsonEncoder.encode(value: String) {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/IterableJsonEncoderTestCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object IterableJsonEncoderTestCodec : AbstractJsonEncoderCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, BooleanJsonCodec, NumberJsonCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonEncoder.encode(value: Iterable<*>) = 11 | IterableJsonEncoderCodec.run { encode(value) } 12 | 13 | 14 | object NonRecursive : AbstractJsonEncoderCodec, JsonCodingContext>( 15 | additionalProviders = listOf(BooleanJsonCodec, IntJsonCodec, StringJsonCodec) 16 | ) { 17 | 18 | override fun JsonEncoder.encode(value: Iterable<*>) = 19 | IterableJsonEncoderCodec.nonRecursive.run { encode(value) } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /coding/tests-jvm/model/YearMonthDay.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal data class YearMonthDay( 5 | val year: Int, 6 | val month: Int, 7 | val day: Int 8 | ) { 9 | 10 | override fun toString() = 11 | buildString { 12 | append(year) 13 | append('-') 14 | 15 | if (month < 10) append('0') 16 | append(month) 17 | append('-') 18 | 19 | if (day < 10) append('0') 20 | append(day) 21 | } 22 | 23 | 24 | companion object { 25 | 26 | private val regex = Regex("^(\\d{1,8})-(\\d{1,2})-(\\d{1,2})") 27 | 28 | 29 | fun parse(string: String): YearMonthDay? { 30 | val result = regex.matchEntire(string) ?: return null 31 | 32 | return YearMonthDay( 33 | year = result.groupValues[1].toInt(), 34 | month = result.groupValues[2].toInt(), 35 | day = result.groupValues[3].toInt() 36 | ) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/ListJsonTestDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object ListJsonTestDecoderCodec : AbstractJsonDecoderCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, BooleanJsonCodec, NumberJsonCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 11 | ListJsonDecoderCodec.run { decode(valueType) } 12 | 13 | 14 | object NonRecursive : AbstractJsonDecoderCodec, JsonCodingContext>( 15 | additionalProviders = listOf(BooleanJsonCodec, IntJsonCodec, StringJsonCodec) 16 | ) { 17 | 18 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 19 | ListJsonDecoderCodec.nonRecursive.run { decode(valueType) } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/SetJsonTestDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object SetJsonTestDecoderCodec : AbstractJsonDecoderCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, BooleanJsonCodec, ListJsonDecoderCodec, NumberJsonCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 11 | SetJsonDecoderCodec.run { decode(valueType) } 12 | 13 | 14 | object NonRecursive : AbstractJsonDecoderCodec, JsonCodingContext>( 15 | additionalProviders = listOf(BooleanJsonCodec, NumberJsonCodec, StringJsonCodec) 16 | ) { 17 | 18 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 19 | SetJsonDecoderCodec.nonRecursive.run { decode(valueType) } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /coding/tests-jvm/codecs/IntRangeJsonCodecTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class IntRangeJsonCodecTest { 9 | 10 | val codecs = JsonCodecProvider( 11 | IntJsonCodec, 12 | IntRangeJsonCodec 13 | ) 14 | 15 | val parser = JsonCodingParser.builder() 16 | .decodingWith(codecs, base = null) 17 | .build() 18 | 19 | val serializer = JsonCodingSerializer.builder() 20 | .encodingWith(codecs, base = null) 21 | .build() 22 | 23 | 24 | @Test 25 | fun testDecodesIntRange() { 26 | expect(parser.parseValueOfType("""{"start":0,"endInclusive":1}""")) 27 | .toBe(IntRange(0, 1)) 28 | } 29 | 30 | 31 | @Test 32 | fun testEncodesIntRange() { 33 | expect(serializer.serializeValue(IntRange(0, 1))) 34 | .toBe("""{"start":0,"endInclusive":1}""") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ktor-client/sources-jvm/FluidJsonSerializer.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import io.ktor.client.features.json.JsonSerializer 4 | import io.ktor.content.TextContent 5 | import io.ktor.http.* 6 | import io.ktor.http.content.* 7 | import io.ktor.util.reflect.* 8 | import io.ktor.utils.io.core.* 9 | 10 | 11 | public class FluidJsonSerializer( 12 | private val parser: JsonCodingParser<*> = JsonCodingParser.nonRecursive, 13 | private val serializer: JsonCodingSerializer = JsonCodingSerializer.nonRecursive, 14 | ) : JsonSerializer { 15 | 16 | @Suppress("INVISIBLE_MEMBER") 17 | override fun read(type: TypeInfo, body: Input): Any = 18 | parser.parseValueOfType(body.readText(), JsonCodingType.of(type.reifiedType)) 19 | 20 | 21 | override fun write(data: Any, contentType: ContentType): OutgoingContent = 22 | TextContent(serializer.serializeValue(data), contentType) 23 | } 24 | -------------------------------------------------------------------------------- /coding/tests-jvm/codecs/LongRangeJsonCodecTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class LongRangeJsonCodecTest { 9 | 10 | val codecs = JsonCodecProvider( 11 | LongJsonCodec, 12 | LongRangeJsonCodec 13 | ) 14 | 15 | val parser = JsonCodingParser.builder() 16 | .decodingWith(codecs, base = null) 17 | .build() 18 | 19 | val serializer = JsonCodingSerializer.builder() 20 | .encodingWith(codecs, base = null) 21 | .build() 22 | 23 | 24 | @Test 25 | fun testDecodesLongRange() { 26 | expect(parser.parseValueOfType("""{"start":0,"endInclusive":1}""")) 27 | .toBe(LongRange(0L, 1L)) 28 | } 29 | 30 | 31 | @Test 32 | fun encodesLongRange() { 33 | expect(serializer.serializeValue(LongRange(0L, 1L))) 34 | .toBe("""{"start":0,"endInclusive":1}""") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/CodingImplementationsJava8.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER") 2 | 3 | package io.fluidsonic.json.dynamic 4 | 5 | import io.fluidsonic.json.* 6 | 7 | 8 | @Suppress("unused") 9 | internal open class CodingImplementationsJava8 : CodingImplementationsJava7() { 10 | 11 | override fun extendedCodecProviders() = 12 | super.extendedCodecProviders() + listOf( 13 | DayOfWeekJsonCodec, 14 | DurationJsonCodec, 15 | InstantJsonCodec, 16 | LocalDateJsonCodec, 17 | LocalDateTimeJsonCodec, 18 | LocalTimeJsonCodec, 19 | MonthDayJsonCodec, 20 | MonthJsonCodec, 21 | OffsetDateTimeJsonCodec, 22 | OffsetTimeJsonCodec, 23 | PeriodJsonCodec, 24 | YearJsonCodec, 25 | YearMonthJsonCodec, 26 | ZonedDateTimeJsonCodec, 27 | ZoneIdJsonCodec, 28 | ZoneOffsetJsonCodec 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /coding/sources-jvm/JsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | 5 | 6 | public interface JsonCodec 7 | : JsonDecoderCodec, JsonEncoderCodec { 8 | 9 | override val decodableType: JsonCodingType 10 | 11 | 12 | override val encodableClass: KClass 13 | get() = decodableType.rawClass 14 | 15 | 16 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? = 17 | super.decoderCodecForType(decodableType) 18 | 19 | 20 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? = 21 | super.encoderCodecForClass(encodableClass) 22 | 23 | 24 | public companion object 25 | } 26 | -------------------------------------------------------------------------------- /coding/sources-jvm/JsonEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | 5 | 6 | public interface JsonEncoderCodec : JsonCodecProvider { 7 | 8 | public val encodableClass: KClass 9 | 10 | 11 | public fun JsonEncoder.encode(value: Value) 12 | 13 | 14 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? = 15 | null 16 | 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? = 20 | if (encodableClass.isAssignableOrBoxableTo(this.encodableClass)) 21 | this as JsonEncoderCodec 22 | else 23 | null 24 | 25 | 26 | public companion object 27 | } 28 | -------------------------------------------------------------------------------- /examples/sources-jvm/0012-ParsingLists.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | fun main() { 7 | val parser = JsonCodingParser.default 8 | 9 | // You can parse a List of values in a typesafe way 10 | val value1 = parser.parseValueOfType>("[1, true, []]") 11 | println(value1) 12 | 13 | // You can parse a List of values of a specific type in a typesafe way 14 | val value2 = parser.parseValueOfType>("""["one", "two", null]""") 15 | println(value2) 16 | 17 | // Note that due to a limitation of Kotlin and the JVM you can specify either `List` or `List` but 18 | // the resulting list can always contain `null` values. This can cause an unexpected `NullPointerException` at 19 | // runtime if the source data contains `null`s. 20 | val value3 = parser.parseValueOfType>("""["one", "two", null]""") 21 | println(value3) 22 | } 23 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/input/codecProvider/CustomContextCodecProvider.kt: -------------------------------------------------------------------------------- 1 | package codecProvider 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | @Json.CodecProvider( 7 | externalTypes = [ 8 | Json.ExternalType(KT30280::class, Json(codecPackageName = "externalType"), targetName = "codecProvider.KT30280"), 9 | Json.ExternalType(KT30280Primitive::class, Json(codecPackageName = "externalType"), targetName = "codecProvider.KT30280Primitive"), 10 | Json.ExternalType(Pair::class, Json( 11 | codecName = "ExternalPairCodec", 12 | codecPackageName = "externalType", 13 | codecVisibility = Json.CodecVisibility.publicRequired 14 | )) 15 | ] 16 | ) 17 | interface CustomContextCodecProvider : JsonCodecProvider 18 | 19 | 20 | interface CustomCodingContext : JsonCodingContext 21 | 22 | inline class KT30280(val value: String) 23 | inline class KT30280Primitive(val value: Double) 24 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/stringData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val stringData: TestData = TestData( 5 | symmetric = mapOf( 6 | "" to "\"\"", 7 | "simple" to "\"simple\"", 8 | "emoji: 🐶" to "\"emoji: 🐶\"", 9 | "\\ \"" to "\"\\\\ \\\"\"", 10 | "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u000B\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u0020" to "\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u000B\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\u0020\"", 11 | " \\ \" / \b \u000C \n \r \t " to "\" \\\\ \\\" / \\b \\f \\n \\r \\t \"" 12 | ), 13 | decodableOnly = mapOf( 14 | "\"\\u0022\"" to "\"", 15 | "\" \\\\ \\\" \\/ \\b \\f \\n \\r \\t \\uD83D\\udc36 \"" to " \\ \" / \b \u000C \n \r \t 🐶 " 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /coding/tests-jvm/codecs/CharRangeJsonCodecTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class CharRangeJsonCodecTest { 9 | 10 | val codecs = JsonCodecProvider( 11 | CharJsonCodec, 12 | CharRangeJsonCodec 13 | ) 14 | 15 | val parser = JsonCodingParser.builder() 16 | .decodingWith(codecs, base = null) 17 | .build() 18 | 19 | val serializer = JsonCodingSerializer.builder() 20 | .encodingWith(codecs, base = null) 21 | .build() 22 | 23 | 24 | @Test 25 | fun testDecodesCharRange() { 26 | expect(parser.parseValueOfType("""{"start":"\u0000","endInclusive":"a"}""")) 27 | .toBe(CharRange(0.toChar(), 'a')) 28 | } 29 | 30 | 31 | @Test 32 | fun testEncodesCharRange() { 33 | expect(serializer.serializeValue(CharRange(0.toChar(), 'a'))) 34 | .toBe("""{"start":"\u0000","endInclusive":"a"}""") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/utility/KotlinpoetTypeNames.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import com.squareup.kotlinpoet.* 4 | 5 | 6 | internal object KotlinpoetTypeNames { 7 | 8 | val any = ANY 9 | val boolean = ClassName("kotlin", "Boolean") 10 | val byte = ClassName("kotlin", "Byte") 11 | val char = ClassName("kotlin", "Char") 12 | val double = ClassName("kotlin", "Double") 13 | val float = ClassName("kotlin", "Float") 14 | val int = ClassName("kotlin", "Int") 15 | val long = ClassName("kotlin", "Long") 16 | val short = ClassName("kotlin", "Short") 17 | val string = ClassName("kotlin", "String") 18 | 19 | val nullableAny = any.copy(nullable = true) 20 | 21 | val basic = setOf( 22 | boolean, 23 | byte, 24 | char, 25 | double, 26 | float, 27 | int, 28 | long, 29 | short 30 | ) 31 | } 32 | 33 | 34 | internal val TypeName.isPrimitive 35 | get() = KotlinpoetTypeNames.basic.contains(this) 36 | -------------------------------------------------------------------------------- /annotation-processor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.fluidsonic.gradle.* 2 | 3 | fluidLibraryModule(description = "A JSON library written in pure Kotlin (annotation processor)") { 4 | targets { 5 | jvm { 6 | withJava() 7 | 8 | @Suppress("SpellCheckingInspection") 9 | dependencies { 10 | implementation(project(":fluid-json-annotations")) 11 | implementation(project(":fluid-json-coding")) 12 | implementation(kotlin("reflect")) 13 | implementation(fluid("meta", "0.14.0")) 14 | implementation("com.google.auto:auto-common:1.2.2") 15 | implementation("com.google.auto.service:auto-service:1.1.1") 16 | implementation("com.squareup:kotlinpoet:1.14.2") 17 | 18 | kapt("com.google.auto.service:auto-service:1.1.1") 19 | } 20 | 21 | testDependencies { 22 | implementation(fluid("compiler", "0.13.0")) 23 | } 24 | } 25 | } 26 | } 27 | 28 | tasks.named("dokkaHtml") { 29 | dependsOn("kaptKotlinJvm") 30 | } 31 | 32 | kotlin { 33 | jvmToolchain(8) 34 | } 35 | -------------------------------------------------------------------------------- /examples/sources-jvm/0013-ParsingMaps.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | fun main() { 7 | val parser = JsonCodingParser.default 8 | 9 | // You can parse a Map of keys and values in a typesafe way 10 | val value1 = parser.parseValueOfType>("""{ "one": 1, "two": 2, "three": 3 }""") 11 | println(value1) 12 | 13 | // You can parse a Map of keys and values of a specific value type in a typesafe way 14 | val value2 = parser.parseValueOfType>("""{ "one": "one", "two": "two", "three": null }""") 15 | println(value2) 16 | 17 | // Note that due to a limitation of Kotlin and the JVM you can specify either `Map` or 18 | // `Map` but the resulting map can always contain `null` keys and values. This can cause an 19 | // unexpected `NullPointerException` at runtime if the source data contains `null`s. 20 | val value3 = parser.parseValueOfType>("""{ "one": "one", "two": "two", "three": null }""") 21 | println(value3) 22 | } 23 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/decoding/DefaultObjectJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonEncoderCodec 5 | import io.fluidsonic.json.JsonEncoder 6 | import io.fluidsonic.json.writeBooleanOrNull 7 | import io.fluidsonic.json.writeByteOrNull 8 | import io.fluidsonic.json.writeCharOrNull 9 | import io.fluidsonic.json.writeDoubleOrNull 10 | import io.fluidsonic.json.writeFloatOrNull 11 | import io.fluidsonic.json.writeIntOrNull 12 | import io.fluidsonic.json.writeIntoMap 13 | import io.fluidsonic.json.writeLongOrNull 14 | import io.fluidsonic.json.writeMapElement 15 | import io.fluidsonic.json.writeShortOrNull 16 | import io.fluidsonic.json.writeStringOrNull 17 | import io.fluidsonic.json.writeValueOrNull 18 | 19 | internal object DefaultObjectJsonCodec : 20 | AbstractJsonEncoderCodec() { 21 | override fun JsonEncoder.encode(`value`: DefaultObject) { 22 | writeIntoMap { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/decoding/AutomaticObjectJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonEncoderCodec 5 | import io.fluidsonic.json.JsonEncoder 6 | import io.fluidsonic.json.writeBooleanOrNull 7 | import io.fluidsonic.json.writeByteOrNull 8 | import io.fluidsonic.json.writeCharOrNull 9 | import io.fluidsonic.json.writeDoubleOrNull 10 | import io.fluidsonic.json.writeFloatOrNull 11 | import io.fluidsonic.json.writeIntOrNull 12 | import io.fluidsonic.json.writeIntoMap 13 | import io.fluidsonic.json.writeLongOrNull 14 | import io.fluidsonic.json.writeMapElement 15 | import io.fluidsonic.json.writeShortOrNull 16 | import io.fluidsonic.json.writeStringOrNull 17 | import io.fluidsonic.json.writeValueOrNull 18 | 19 | internal object AutomaticObjectJsonCodec : 20 | AbstractJsonEncoderCodec() { 21 | override fun JsonEncoder.encode(`value`: AutomaticObject) { 22 | writeIntoMap { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/decoding/NoneJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.decoding 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonEncoderCodec 5 | import io.fluidsonic.json.JsonEncoder 6 | import io.fluidsonic.json.writeBooleanOrNull 7 | import io.fluidsonic.json.writeByteOrNull 8 | import io.fluidsonic.json.writeCharOrNull 9 | import io.fluidsonic.json.writeDoubleOrNull 10 | import io.fluidsonic.json.writeFloatOrNull 11 | import io.fluidsonic.json.writeIntOrNull 12 | import io.fluidsonic.json.writeIntoMap 13 | import io.fluidsonic.json.writeLongOrNull 14 | import io.fluidsonic.json.writeMapElement 15 | import io.fluidsonic.json.writeShortOrNull 16 | import io.fluidsonic.json.writeStringOrNull 17 | import io.fluidsonic.json.writeValueOrNull 18 | 19 | internal object NoneJsonCodec : AbstractJsonEncoderCodec() { 20 | override fun JsonEncoder.encode(`value`: None) { 21 | writeIntoMap { 22 | writeMapElement("value", string = value.`value`) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/classes/ObjectJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonEncoderCodec 5 | import io.fluidsonic.json.JsonEncoder 6 | import io.fluidsonic.json.writeBooleanOrNull 7 | import io.fluidsonic.json.writeByteOrNull 8 | import io.fluidsonic.json.writeCharOrNull 9 | import io.fluidsonic.json.writeDoubleOrNull 10 | import io.fluidsonic.json.writeFloatOrNull 11 | import io.fluidsonic.json.writeIntOrNull 12 | import io.fluidsonic.json.writeIntoMap 13 | import io.fluidsonic.json.writeLongOrNull 14 | import io.fluidsonic.json.writeMapElement 15 | import io.fluidsonic.json.writeShortOrNull 16 | import io.fluidsonic.json.writeStringOrNull 17 | import io.fluidsonic.json.writeValueOrNull 18 | 19 | internal object ObjectJsonCodec : AbstractJsonEncoderCodec() { 20 | override fun JsonEncoder.encode(`value`: Object) { 21 | writeIntoMap { 22 | writeMapElement("value", string = value.`value`) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/StandardDecoder.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | internal class StandardDecoder( 5 | override val context: Context, 6 | private val codecProvider: JsonCodecProvider, 7 | source: JsonReader 8 | ) : JsonDecoder, JsonReader by source { 9 | 10 | override fun readValue() = 11 | super.readValue() 12 | 13 | 14 | override fun readValueOfType(valueType: JsonCodingType) = 15 | codecProvider.decoderCodecForType(valueType) 16 | ?.run { 17 | try { 18 | isolateValueRead { 19 | decode(valueType = valueType) 20 | } 21 | } 22 | catch (e: JsonException) { 23 | // TODO remove .java once KT-28418 is fixed 24 | e.addSuppressed(JsonException.Parsing("… when decoding value of type $valueType using ${this::class.java.name}")) 25 | throw e 26 | } 27 | } 28 | ?: throw JsonException.Parsing( 29 | message = "No decoder codec registered for $valueType", 30 | offset = offset, 31 | path = path 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/CollectionJsonTestCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object CollectionJsonTestCodec : AbstractJsonCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 11 | CollectionJsonCodec.run { decode(valueType) } 12 | 13 | 14 | override fun JsonEncoder.encode(value: Collection<*>) = 15 | CollectionJsonCodec.run { encode(value) } 16 | 17 | 18 | object NonRecursive : AbstractJsonCodec, JsonCodingContext>( 19 | additionalProviders = listOf(StringJsonCodec) 20 | ) { 21 | 22 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 23 | CollectionJsonCodec.nonRecursive.run { decode(valueType) } 24 | 25 | 26 | override fun JsonEncoder.encode(value: Collection<*>) = 27 | CollectionJsonCodec.nonRecursive.run { encode(value) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/StandardEncoder.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | internal class StandardEncoder( 5 | override val context: Context, 6 | private val codecProvider: JsonCodecProvider, 7 | private val destination: JsonWriter 8 | ) : JsonEncoder, JsonWriter by destination { 9 | 10 | @Suppress("UNCHECKED_CAST") 11 | override fun writeValue(value: Any) { 12 | withErrorChecking { 13 | (codecProvider.encoderCodecForClass(value::class) as JsonEncoderCodec?) 14 | ?.run { 15 | try { 16 | isolateValueWrite { 17 | encode(value = value) 18 | } 19 | } 20 | catch (e: JsonException) { 21 | // TODO remove .java once KT-28418 is fixed 22 | e.addSuppressed(JsonException.Serialization("… when encoding value of ${value::class} using ${this::class.java.name}: $value")) 23 | throw e 24 | } 25 | } 26 | ?: throw JsonException.Serialization( 27 | message = "No encoder codec registered for ${value::class}: $value", 28 | path = path 29 | ) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/MapJsonTestCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object MapJsonTestCodec : AbstractJsonCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, BooleanJsonCodec, NumberJsonCodec, StringJsonCodec, YearMonthDayCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 11 | MapJsonCodec.run { decode(valueType) } 12 | 13 | 14 | override fun JsonEncoder.encode(value: Map<*, *>) = 15 | MapJsonCodec.run { encode(value) } 16 | 17 | 18 | object NonRecursive : AbstractJsonCodec, JsonCodingContext>( 19 | additionalProviders = listOf(BooleanJsonCodec, IntJsonCodec, StringJsonCodec) 20 | ) { 21 | 22 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 23 | MapJsonCodec.nonRecursive.run { decode(valueType) } 24 | 25 | 26 | override fun JsonEncoder.encode(value: Map) = 27 | MapJsonCodec.nonRecursive.run { encode(value) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/NonRecursiveJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | 5 | 6 | internal abstract class NonRecursiveJsonCodec private constructor( 7 | private val encoder: NonRecursiveJsonEncoderCodec 8 | ) : 9 | NonRecursiveJsonDecoderCodec(), 10 | JsonCodec, 11 | JsonEncoderCodec by encoder { 12 | 13 | 14 | override val encodableClass 15 | get() = encoder.encodableClass 16 | 17 | 18 | override fun decoderCodecForType(decodableType: JsonCodingType) = 19 | super.decoderCodecForType(decodableType) 20 | 21 | 22 | override fun encoderCodecForClass(encodableClass: KClass) = 23 | encoder.encoderCodecForClass(encodableClass) 24 | 25 | 26 | companion object { 27 | 28 | inline fun create(): JsonCodec = 29 | object : NonRecursiveJsonCodec( 30 | encoder = NonRecursiveJsonEncoderCodec(encodableClass = Value::class) 31 | ) {} 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/UniverseCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object UniverseCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Universe { 9 | var jaegers: List? = null 10 | var kaijus: List? = null 11 | 12 | readFromMapByElementValue { key -> 13 | when (key) { 14 | Keys.jaegers -> jaegers = readValueOfType() 15 | Keys.kaijus -> kaijus = readValueOfType() 16 | else -> skipValue() 17 | } 18 | } 19 | 20 | return Universe( 21 | jaegers = jaegers ?: missingPropertyError("jaegers"), 22 | kaijus = kaijus ?: missingPropertyError("kaijus") 23 | ) 24 | } 25 | 26 | 27 | override fun JsonEncoder.encode(value: Universe) { 28 | writeIntoMap { 29 | writeMapElement(Keys.jaegers, value = value.jaegers) 30 | writeMapElement(Keys.kaijus, value = value.kaijus) 31 | } 32 | } 33 | 34 | 35 | private object Keys { 36 | 37 | const val jaegers = "jaegers" 38 | const val kaijus = "kaijus" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /basic/tests-jvm/dummys/DummyJsonReader.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal open class DummyJsonReader : JsonReader { 7 | 8 | override fun beginValueIsolation() = JsonDepth(0) 9 | override fun close(): Unit = error("") 10 | override val depth: JsonDepth get() = error("") 11 | override fun endValueIsolation(depth: JsonDepth) {} 12 | override val isInValueIsolation: Boolean get() = false 13 | override val nextToken: JsonToken? get() = error("") 14 | override val offset: Int get() = error("") 15 | override val path: JsonPath get() = error("") 16 | override fun readBoolean(): Boolean = error("") 17 | override fun readDouble(): Double = error("") 18 | override fun readListEnd(): Unit = error("") 19 | override fun readListStart(): Unit = error("") 20 | override fun readLong(): Long = error("") 21 | override fun readMapEnd(): Unit = error("") 22 | override fun readMapStart(): Unit = error("") 23 | override fun readNull(): Nothing? = error("") 24 | override fun readNumber(): Number = error("") 25 | override fun readString(): String = error("") 26 | override fun terminate(): Unit = error("") 27 | } 28 | -------------------------------------------------------------------------------- /coding/sources-jvm/utility/KType.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | import kotlin.reflect.jvm.internal.* 5 | 6 | // from kotlin-reflect, modified, probably wrong in some edge cases, close enough in order to avoid kotlin-reflect dependency for now 7 | 8 | 9 | /** 10 | * Returns the [KClass] instance representing the runtime class to which this type is erased to on JVM. 11 | */ 12 | @SinceKotlin("1.1") 13 | public val KType.jvmErasure: KClass<*> 14 | get() = classifier?.jvmErasure ?: throw KotlinReflectionInternalError("Cannot calculate JVM erasure for type: $this") 15 | 16 | 17 | internal val KClassifier.jvmErasure: KClass<*> 18 | get() = when (this) { 19 | is KClass<*> -> this 20 | is KTypeParameter -> { 21 | val bounds = upperBounds 22 | val representativeBoundErasures = bounds.mapNotNull { it.classifier?.jvmErasure } 23 | representativeBoundErasures.firstOrNull { !(it.java.isInterface || it.java.isAnnotation) } 24 | ?: representativeBoundErasures.firstOrNull() 25 | ?: Any::class 26 | } 27 | else -> throw KotlinReflectionInternalError("Cannot calculate JVM erasure for type: $this") 28 | } 29 | -------------------------------------------------------------------------------- /coding/tests-jvm/dummys/DummyJsonReader.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal open class DummyJsonReader : JsonReader { 7 | 8 | override fun beginValueIsolation() = JsonDepth(0) 9 | override fun close(): Unit = error("") 10 | override val depth: JsonDepth get() = error("") 11 | override fun endValueIsolation(depth: JsonDepth) {} 12 | override val isInValueIsolation: Boolean get() = false 13 | override val nextToken: JsonToken? get() = error("") 14 | override val offset: Int get() = error("") 15 | override val path: JsonPath get() = error("") 16 | override fun readBoolean(): Boolean = error("") 17 | override fun readDouble(): Double = error("") 18 | override fun readListEnd(): Unit = error("") 19 | override fun readListStart(): Unit = error("") 20 | override fun readLong(): Long = error("") 21 | override fun readMapEnd(): Unit = error("") 22 | override fun readMapStart(): Unit = error("") 23 | override fun readNull(): Nothing? = error("") 24 | override fun readNumber(): Number = error("") 25 | override fun readString(): String = error("") 26 | override fun terminate(): Unit = error("") 27 | } 28 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/intData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val intData: TestData = TestData( 5 | symmetric = mapOf( 6 | Int.MIN_VALUE to "-2147483648", 7 | -1 to "-1", 8 | 0 to "0", 9 | 1 to "1", 10 | Int.MAX_VALUE to "2147483647" 11 | ), 12 | decodableOnly = mapOf( 13 | "-1e20000" to Int.MIN_VALUE, 14 | "-1000000000000000000000000000000" to Int.MIN_VALUE, 15 | "-1.0e+2" to -100, 16 | "-1.0e2" to -100, 17 | "-1e+2" to -100, 18 | "-1e2" to -100, 19 | "-100" to -100, 20 | "-1000000000000000000000000000000e-30" to -1, 21 | "-1.9" to -1, 22 | "-1.1" to -1, 23 | "-1E0" to -1, 24 | "-1.0e-2" to 0, 25 | "-1e-2" to 0, 26 | "-1e-20000" to 0, 27 | "-0.000" to 0, 28 | "-0" to 0, 29 | "0" to 0, 30 | "0.000" to 0, 31 | "1e-20000" to 0, 32 | "1e-2" to 0, 33 | "1.0e-2" to 0, 34 | "1E0" to 1, 35 | "1.1" to 1, 36 | "1.9" to 1, 37 | "1000000000000000000000000000000e-30" to 1, 38 | "100" to 100, 39 | "1e2" to 100, 40 | "1e+2" to 100, 41 | "1.0e2" to 100, 42 | "1.0e+2" to 100, 43 | "1000000000000000000000000000000" to Int.MAX_VALUE, 44 | "1e20000" to Int.MAX_VALUE 45 | ) 46 | ) 47 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/DayOfWeekJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object DayOfWeekJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): DayOfWeek = 9 | readString().let { raw -> 10 | when (raw) { 11 | "monday" -> DayOfWeek.MONDAY 12 | "tuesday" -> DayOfWeek.TUESDAY 13 | "wednesday" -> DayOfWeek.WEDNESDAY 14 | "thursday" -> DayOfWeek.THURSDAY 15 | "friday" -> DayOfWeek.FRIDAY 16 | "saturday" -> DayOfWeek.SATURDAY 17 | "sunday" -> DayOfWeek.SUNDAY 18 | else -> invalidValueError("weekday name of 'monday' through 'sunday' expected, got '$raw'") 19 | } 20 | } 21 | 22 | 23 | override fun JsonEncoder.encode(value: DayOfWeek): Unit = 24 | writeString(when (value) { 25 | DayOfWeek.MONDAY -> "monday" 26 | DayOfWeek.TUESDAY -> "tuesday" 27 | DayOfWeek.WEDNESDAY -> "wednesday" 28 | DayOfWeek.THURSDAY -> "thursday" 29 | DayOfWeek.FRIDAY -> "friday" 30 | DayOfWeek.SATURDAY -> "saturday" 31 | DayOfWeek.SUNDAY -> "sunday" 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /basic/tests-jvm/dummys/DummyJsonWriter.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal open class DummyJsonWriter : JsonWriter { 7 | override fun beginValueIsolation() = JsonDepth(0) 8 | override val depth: JsonDepth get() = error("") 9 | override fun endValueIsolation(depth: JsonDepth) {} 10 | override val isInValueIsolation: Boolean get() = error("") 11 | override val isErrored: Boolean get() = error("") 12 | override val path: JsonPath get() = error("") 13 | override fun markAsErrored(): Unit = error("") 14 | override fun terminate(): Unit = error("") 15 | override fun writeBoolean(value: Boolean): Unit = error("") 16 | override fun writeDouble(value: Double): Unit = error("") 17 | override fun writeListEnd(): Unit = error("") 18 | override fun writeListStart(): Unit = error("") 19 | override fun writeLong(value: Long): Unit = error("") 20 | override fun writeMapEnd(): Unit = error("") 21 | override fun writeMapStart(): Unit = error("") 22 | override fun writeNull(): Unit = error("") 23 | override fun writeString(value: String): Unit = error("") 24 | override fun close(): Unit = error("") 25 | override fun flush(): Unit = error("") 26 | } 27 | -------------------------------------------------------------------------------- /coding/tests-jvm/JsonCodingTypeTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class JsonCodingTypeTest { 8 | 9 | @Test 10 | fun testGenericTypeParametersWithMultipleUpperBounds() { 11 | jsonCodingType>() 12 | } 13 | 14 | 15 | @Test 16 | @Suppress("UNCHECKED_CAST") 17 | fun testKClassBasedCreation() { 18 | expect(jsonCodingType(List::class)).toBe(jsonCodingType()) 19 | expect(jsonCodingType(Array::class)).toBe(jsonCodingType()) 20 | expect(jsonCodingType(Array::class, String::class)).toBe(jsonCodingType()) 21 | expect(jsonCodingType(Map::class)).toBe(jsonCodingType()) 22 | expect(jsonCodingType(Map::class, String::class, Int::class)).toBe(jsonCodingType>() as JsonCodingType>) 23 | expect(jsonCodingType(String::class)).toBe(jsonCodingType()) 24 | } 25 | 26 | 27 | @Test 28 | fun testRecursiveGenerics() { 29 | expect(jsonCodingType(ClosedRange::class)).toBe(jsonCodingType()) 30 | } 31 | 32 | 33 | private interface Interface1 34 | private interface Interface2 35 | private interface MultipleUpperBounds where T : Interface1, T : Interface2 36 | } 37 | -------------------------------------------------------------------------------- /coding/tests-jvm/utility/codecs/SequenceJsonTestCodec.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal object SequenceJsonTestCodec : AbstractJsonCodec, JsonCodingContext>( 7 | additionalProviders = listOf(AnyJsonDecoderCodec, BooleanJsonCodec, IterableJsonEncoderCodec, ListJsonDecoderCodec, NumberJsonCodec, StringJsonCodec) 8 | ) { 9 | 10 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 11 | SequenceJsonCodec.run { decode(valueType) } 12 | 13 | 14 | override fun JsonEncoder.encode(value: Sequence<*>) = 15 | SequenceJsonCodec.run { encode(value) } 16 | 17 | 18 | object NonRecursive : AbstractJsonCodec, JsonCodingContext>( 19 | additionalProviders = listOf(BooleanJsonCodec, IntJsonCodec, IterableJsonEncoderCodec, StringJsonCodec) 20 | ) { 21 | 22 | override fun JsonDecoder.decode(valueType: JsonCodingType>) = 23 | SequenceJsonCodec.nonRecursive.run { decode(valueType) } 24 | 25 | 26 | override fun JsonEncoder.encode(value: Sequence<*>) = 27 | SequenceJsonCodec.nonRecursive.run { encode(value) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 26 | 27 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/classes/InlineClassJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.classes 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.JsonEncoder 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readIntOrNull 14 | import io.fluidsonic.json.readLongOrNull 15 | import io.fluidsonic.json.readShortOrNull 16 | import io.fluidsonic.json.readStringOrNull 17 | import io.fluidsonic.json.readValueOfType 18 | import io.fluidsonic.json.readValueOfTypeOrNull 19 | import io.fluidsonic.json.writeValueOrNull 20 | 21 | internal object InlineClassJsonCodec : AbstractJsonCodec() { 22 | override fun JsonDecoder.decode(valueType: JsonCodingType): 23 | InlineClass = InlineClass(`value` = readString()) 24 | 25 | override fun JsonEncoder.encode(`value`: InlineClass) { 26 | writeString(value.`value`) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/externalType/KT30280JsonCodec.kt: -------------------------------------------------------------------------------- 1 | package externalType 2 | 3 | import codecProvider.CustomCodingContext 4 | import codecProvider.KT30280 5 | import io.fluidsonic.json.AbstractJsonCodec 6 | import io.fluidsonic.json.JsonCodingType 7 | import io.fluidsonic.json.JsonDecoder 8 | import io.fluidsonic.json.JsonEncoder 9 | import io.fluidsonic.json.readBooleanOrNull 10 | import io.fluidsonic.json.readByteOrNull 11 | import io.fluidsonic.json.readCharOrNull 12 | import io.fluidsonic.json.readDoubleOrNull 13 | import io.fluidsonic.json.readFloatOrNull 14 | import io.fluidsonic.json.readIntOrNull 15 | import io.fluidsonic.json.readLongOrNull 16 | import io.fluidsonic.json.readShortOrNull 17 | import io.fluidsonic.json.readStringOrNull 18 | import io.fluidsonic.json.readValueOfType 19 | import io.fluidsonic.json.readValueOfTypeOrNull 20 | import io.fluidsonic.json.writeValueOrNull 21 | 22 | internal object KT30280JsonCodec : AbstractJsonCodec() { 23 | override fun JsonDecoder.decode(valueType: JsonCodingType): KT30280 = 24 | KT30280(`value` = readString()) 25 | 26 | override fun JsonEncoder.encode(`value`: KT30280) { 27 | writeString(value.`value`) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/representation/SingleValueJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.JsonEncoder 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readIntOrNull 14 | import io.fluidsonic.json.readLongOrNull 15 | import io.fluidsonic.json.readShortOrNull 16 | import io.fluidsonic.json.readStringOrNull 17 | import io.fluidsonic.json.readValueOfType 18 | import io.fluidsonic.json.readValueOfTypeOrNull 19 | import io.fluidsonic.json.writeValueOrNull 20 | 21 | internal object SingleValueJsonCodec : AbstractJsonCodec() { 22 | override fun JsonDecoder.decode(valueType: JsonCodingType): 23 | SingleValue = SingleValue(`value` = readString()) 24 | 25 | override fun JsonEncoder.encode(`value`: SingleValue) { 26 | writeString(value.`value`) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/extended/IntRangeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object IntRangeJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): IntRange { 7 | var endInclusive = 0 8 | var endInclusiveProvided = false 9 | var start = 0 10 | var startProvided = false 11 | 12 | readFromMapByElementValue { key -> 13 | when (key) { 14 | Fields.endInclusive -> { 15 | endInclusive = readInt() 16 | endInclusiveProvided = true 17 | } 18 | Fields.start -> { 19 | start = readInt() 20 | startProvided = true 21 | } 22 | else -> skipValue() 23 | } 24 | } 25 | 26 | if (!startProvided) missingPropertyError(Fields.start) 27 | if (!endInclusiveProvided) missingPropertyError(Fields.endInclusive) 28 | 29 | return start .. endInclusive 30 | } 31 | 32 | 33 | override fun JsonEncoder.encode(value: IntRange) { 34 | writeIntoMap { 35 | writeMapElement(Fields.start, int = value.first) 36 | writeMapElement(Fields.endInclusive, int = value.last) 37 | } 38 | } 39 | 40 | 41 | private object Fields { 42 | 43 | const val endInclusive = "endInclusive" 44 | const val start = "start" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /coding/tests-jvm/StandardCodingSerializerRejectTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class StandardCodingSerializerRejectTest { 8 | 9 | @Test 10 | fun testNonFiniteFloat() { 11 | failToSerialize(Float.NEGATIVE_INFINITY) 12 | failToSerialize(Float.POSITIVE_INFINITY) 13 | failToSerialize(Float.NaN) 14 | } 15 | 16 | 17 | @Test 18 | fun testNonFiniteDouble() { 19 | failToSerialize(Double.NEGATIVE_INFINITY) 20 | failToSerialize(Double.POSITIVE_INFINITY) 21 | failToSerialize(Double.NaN) 22 | } 23 | 24 | 25 | @Test 26 | fun testNonStringMapKeys() { 27 | failToSerialize(mapOf(null to null)) 28 | failToSerialize(mapOf(0 to 0)) 29 | } 30 | 31 | 32 | @Test 33 | fun testUnsupportedClasses() { 34 | failToSerialize(object {}) 35 | } 36 | 37 | 38 | private fun failToSerialize(value: Any?) { 39 | val serializer = StandardCodingSerializer(JsonCodingContext.empty) { destination, context -> 40 | JsonEncoder.builder(context) 41 | .codecs() 42 | .destination(destination) 43 | .build() 44 | } 45 | 46 | try { 47 | serializer.serializeValue(value) 48 | throw AssertionError("should fail with a JsonException") 49 | } 50 | catch (e: JsonException) { 51 | // good 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/extended/LongRangeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object LongRangeJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): LongRange { 7 | var endInclusive = 0L 8 | var endInclusiveProvided = false 9 | var start = 0L 10 | var startProvided = false 11 | 12 | readFromMapByElementValue { key -> 13 | when (key) { 14 | Fields.endInclusive -> { 15 | endInclusive = readLong() 16 | endInclusiveProvided = true 17 | } 18 | Fields.start -> { 19 | start = readLong() 20 | startProvided = true 21 | } 22 | else -> skipValue() 23 | } 24 | } 25 | 26 | if (!startProvided) missingPropertyError(Fields.start) 27 | if (!endInclusiveProvided) missingPropertyError(Fields.endInclusive) 28 | 29 | return start .. endInclusive 30 | } 31 | 32 | 33 | override fun JsonEncoder.encode(value: LongRange) { 34 | writeIntoMap { 35 | writeMapElement(Fields.start, long = value.first) 36 | writeMapElement(Fields.endInclusive, long = value.last) 37 | } 38 | } 39 | 40 | 41 | private object Fields { 42 | 43 | const val endInclusive = "endInclusive" 44 | const val start = "start" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /basic/sources-jvm/JsonPath.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public class JsonPath(elements: List = emptyList()) { 5 | 6 | public val elements: List = elements.toList() 7 | 8 | override fun equals(other: Any?): Boolean = (this === other || (other is JsonPath && elements == other.elements)) 9 | override fun hashCode(): Int = elements.hashCode() 10 | 11 | override fun toString(): String = 12 | if (elements.isNotEmpty()) 13 | elements.joinToString(separator = "", prefix = "") 14 | else 15 | "" 16 | 17 | 18 | public companion object { 19 | 20 | public val root: JsonPath = JsonPath() 21 | } 22 | 23 | 24 | public sealed class Element { 25 | 26 | public class ListIndex(public val value: Int) : Element() { 27 | 28 | override fun equals(other: Any?): Boolean = (this === other || (other is ListIndex && value == other.value)) 29 | override fun hashCode(): Int = value 30 | override fun toString(): String = "[$value]" 31 | } 32 | 33 | 34 | public class MapKey(public val value: String) : Element() { 35 | 36 | override fun equals(other: Any?): Boolean = (this === other || (other is MapKey && value == other.value)) 37 | override fun hashCode(): Int = value.hashCode() 38 | override fun toString(): String = ".\"$value\"" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/extended/CharRangeJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public object CharRangeJsonCodec : AbstractJsonCodec() { 5 | 6 | override fun JsonDecoder.decode(valueType: JsonCodingType): CharRange { 7 | var endInclusive = 0.toChar() 8 | var endInclusiveProvided = false 9 | var start = 0.toChar() 10 | var startProvided = false 11 | 12 | readFromMapByElementValue { key -> 13 | when (key) { 14 | Fields.endInclusive -> { 15 | endInclusive = readChar() 16 | endInclusiveProvided = true 17 | } 18 | Fields.start -> { 19 | start = readChar() 20 | startProvided = true 21 | } 22 | else -> skipValue() 23 | } 24 | } 25 | 26 | if (!startProvided) missingPropertyError(Fields.start) 27 | if (!endInclusiveProvided) missingPropertyError(Fields.endInclusive) 28 | 29 | return start .. endInclusive 30 | } 31 | 32 | 33 | override fun JsonEncoder.encode(value: CharRange) { 34 | writeIntoMap { 35 | writeMapElement(Fields.start, char = value.first) 36 | writeMapElement(Fields.endInclusive, char = value.last) 37 | } 38 | } 39 | 40 | 41 | private object Fields { 42 | 43 | const val endInclusive = "endInclusive" 44 | const val start = "start" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/externalType/KT30280PrimitiveJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package externalType 2 | 3 | import codecProvider.CustomCodingContext 4 | import codecProvider.KT30280Primitive 5 | import io.fluidsonic.json.AbstractJsonCodec 6 | import io.fluidsonic.json.JsonCodingType 7 | import io.fluidsonic.json.JsonDecoder 8 | import io.fluidsonic.json.JsonEncoder 9 | import io.fluidsonic.json.readBooleanOrNull 10 | import io.fluidsonic.json.readByteOrNull 11 | import io.fluidsonic.json.readCharOrNull 12 | import io.fluidsonic.json.readDoubleOrNull 13 | import io.fluidsonic.json.readFloatOrNull 14 | import io.fluidsonic.json.readIntOrNull 15 | import io.fluidsonic.json.readLongOrNull 16 | import io.fluidsonic.json.readShortOrNull 17 | import io.fluidsonic.json.readStringOrNull 18 | import io.fluidsonic.json.readValueOfType 19 | import io.fluidsonic.json.readValueOfTypeOrNull 20 | import io.fluidsonic.json.writeValueOrNull 21 | 22 | internal object KT30280PrimitiveJsonCodec : 23 | AbstractJsonCodec() { 24 | override fun JsonDecoder.decode(valueType: JsonCodingType): 25 | KT30280Primitive = KT30280Primitive(`value` = readDouble()) 26 | 27 | override fun JsonEncoder.encode(`value`: KT30280Primitive) { 28 | writeDouble(value.`value`) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/representation/AutomaticSingleValueJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.JsonEncoder 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readIntOrNull 14 | import io.fluidsonic.json.readLongOrNull 15 | import io.fluidsonic.json.readShortOrNull 16 | import io.fluidsonic.json.readStringOrNull 17 | import io.fluidsonic.json.readValueOfType 18 | import io.fluidsonic.json.readValueOfTypeOrNull 19 | import io.fluidsonic.json.writeValueOrNull 20 | 21 | internal object AutomaticSingleValueJsonCodec : 22 | AbstractJsonCodec() { 23 | override 24 | fun JsonDecoder.decode(valueType: JsonCodingType): 25 | AutomaticSingleValue = AutomaticSingleValue(`value` = readString()) 26 | 27 | override fun JsonEncoder.encode(`value`: AutomaticSingleValue) { 28 | writeString(value.`value`) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/representation/SingleValueNullableJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.JsonEncoder 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readIntOrNull 14 | import io.fluidsonic.json.readLongOrNull 15 | import io.fluidsonic.json.readShortOrNull 16 | import io.fluidsonic.json.readStringOrNull 17 | import io.fluidsonic.json.readValueOfType 18 | import io.fluidsonic.json.readValueOfTypeOrNull 19 | import io.fluidsonic.json.writeValueOrNull 20 | 21 | internal object SingleValueNullableJsonCodec : 22 | AbstractJsonCodec() { 23 | override 24 | fun JsonDecoder.decode(valueType: JsonCodingType): 25 | SingleValueNullable = SingleValueNullable(`value` = readValueOfTypeOrNull()) 26 | 27 | override fun JsonEncoder.encode(`value`: SingleValueNullable) { 28 | writeValueOrNull(value.`value`) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /coding-jdk8/sources-jvm/codecs/extended/MonthJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.time.* 4 | 5 | 6 | public object MonthJsonCodec : AbstractJsonCodec() { 7 | 8 | override fun JsonDecoder.decode(valueType: JsonCodingType): Month = 9 | readString().let { raw -> 10 | when (raw) { 11 | "january" -> Month.JANUARY 12 | "february" -> Month.FEBRUARY 13 | "march" -> Month.MARCH 14 | "april" -> Month.APRIL 15 | "may" -> Month.MAY 16 | "june" -> Month.JUNE 17 | "july" -> Month.JULY 18 | "august" -> Month.AUGUST 19 | "september" -> Month.SEPTEMBER 20 | "october" -> Month.OCTOBER 21 | "november" -> Month.NOVEMBER 22 | "december" -> Month.DECEMBER 23 | else -> invalidValueError("month name of 'january' through 'december' expected, got '$raw'") 24 | } 25 | } 26 | 27 | 28 | override fun JsonEncoder.encode(value: Month): Unit = 29 | writeString(when (value) { 30 | Month.JANUARY -> "january" 31 | Month.FEBRUARY -> "february" 32 | Month.MARCH -> "march" 33 | Month.APRIL -> "april" 34 | Month.MAY -> "may" 35 | Month.JUNE -> "june" 36 | Month.JULY -> "july" 37 | Month.AUGUST -> "august" 38 | Month.SEPTEMBER -> "september" 39 | Month.OCTOBER -> "october" 40 | Month.NOVEMBER -> "november" 41 | Month.DECEMBER -> "december" 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/longData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val longData: TestData = TestData( 5 | symmetric = mapOf( 6 | Long.MIN_VALUE to "-9223372036854775808", 7 | -1L to "-1", 8 | 0L to "0", 9 | 1L to "1", 10 | Long.MAX_VALUE to "9223372036854775807" 11 | ), 12 | decodableOnly = mapOf( 13 | "-1e20000" to Long.MIN_VALUE, 14 | "-92233720368547758090" to Long.MIN_VALUE, 15 | "-9223372036854775809" to Long.MIN_VALUE, 16 | "-1000000000000000000000000000000" to Long.MIN_VALUE, 17 | "-1.0e+2" to -100L, 18 | "-1.0e2" to -100L, 19 | "-1e+2" to -100L, 20 | "-1e2" to -100L, 21 | "-100" to -100L, 22 | "-1000000000000000000000000000000e-30" to -1L, 23 | "-1.9" to -1L, 24 | "-1.1" to -1L, 25 | "-1E0" to -1L, 26 | "-1.0e-2" to 0L, 27 | "-1e-2" to 0L, 28 | "-1e-20000" to 0L, 29 | "-0.000" to 0L, 30 | "-0" to 0L, 31 | "0" to 0L, 32 | "0.000" to 0L, 33 | "1e-20000" to 0L, 34 | "1e-2" to 0L, 35 | "1.0e-2" to 0L, 36 | "1E0" to 1L, 37 | "1.1" to 1L, 38 | "1.9" to 1L, 39 | "1000000000000000000000000000000e-30" to 1L, 40 | "100" to 100L, 41 | "1e2" to 100L, 42 | "1e+2" to 100L, 43 | "1.0e2" to 100L, 44 | "1.0e+2" to 100L, 45 | "9223372036854775808" to Long.MAX_VALUE, 46 | "92233720368547758080" to Long.MAX_VALUE, 47 | "1000000000000000000000000000000" to Long.MAX_VALUE, 48 | "1e20000" to Long.MAX_VALUE 49 | ) 50 | ) 51 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/encoding/NoneJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.encoding 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonDecoderCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.missingPropertyError 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readFromMapByElementValue 14 | import io.fluidsonic.json.readIntOrNull 15 | import io.fluidsonic.json.readLongOrNull 16 | import io.fluidsonic.json.readShortOrNull 17 | import io.fluidsonic.json.readStringOrNull 18 | import io.fluidsonic.json.readValueOfType 19 | import io.fluidsonic.json.readValueOfTypeOrNull 20 | import kotlin.String 21 | 22 | internal object NoneJsonCodec : AbstractJsonDecoderCodec() { 23 | override fun JsonDecoder.decode(valueType: JsonCodingType): None { 24 | var _value1: String? = null 25 | 26 | readFromMapByElementValue { key -> 27 | when (key) { 28 | "value1" -> _value1 = readString() 29 | else -> skipValue() 30 | } 31 | } 32 | 33 | return None( 34 | value1 = _value1 ?: missingPropertyError("value1") 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /annotation-processor/test-cases/1/output-expected/json/representation/SingleValueGenericJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package json.representation 2 | 3 | import codecProvider.CustomCodingContext 4 | import io.fluidsonic.json.AbstractJsonCodec 5 | import io.fluidsonic.json.JsonCodingType 6 | import io.fluidsonic.json.JsonDecoder 7 | import io.fluidsonic.json.JsonEncoder 8 | import io.fluidsonic.json.readBooleanOrNull 9 | import io.fluidsonic.json.readByteOrNull 10 | import io.fluidsonic.json.readCharOrNull 11 | import io.fluidsonic.json.readDoubleOrNull 12 | import io.fluidsonic.json.readFloatOrNull 13 | import io.fluidsonic.json.readIntOrNull 14 | import io.fluidsonic.json.readLongOrNull 15 | import io.fluidsonic.json.readShortOrNull 16 | import io.fluidsonic.json.readStringOrNull 17 | import io.fluidsonic.json.readValueOfType 18 | import io.fluidsonic.json.readValueOfTypeOrNull 19 | import io.fluidsonic.json.writeValueOrNull 20 | 21 | internal object SingleValueGenericJsonCodec : 22 | AbstractJsonCodec, CustomCodingContext>() { 23 | override 24 | fun JsonDecoder.decode(valueType: JsonCodingType>): 25 | SingleValueGeneric<*> = SingleValueGeneric(`value` = 26 | readValueOfTypeOrNull(valueType.arguments[0]) as SingleValueGeneric.Bound?) 27 | 28 | override fun JsonEncoder.encode(`value`: SingleValueGeneric<*>) { 29 | writeValueOrNull(value.`value`) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /coding/tests-jvm/JsonCodingSerializerTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class JsonCodingSerializerTest { 8 | 9 | @Test 10 | fun testBuilder() { 11 | JsonCodingSerializer.builder() 12 | .encodingWith(BooleanJsonCodec) 13 | .build() 14 | .apply { 15 | // TODO check correct context 16 | expect(serializeValue(true)).toBe("true") 17 | } 18 | 19 | JsonCodingSerializer.builder() 20 | .encodingWith(listOf(BooleanJsonCodec)) 21 | .build() 22 | .apply { 23 | // TODO check correct context 24 | expect(serializeValue(true)).toBe("true") 25 | } 26 | 27 | val testContext = TestCoderContext() 28 | 29 | JsonCodingSerializer.builder(testContext) 30 | .encodingWith() 31 | .build() 32 | .apply { 33 | // TODO check correct context 34 | expect(serializeValue(true)).toBe("true") 35 | } 36 | } 37 | 38 | 39 | @Test 40 | fun testBuilderWithDifferentBaseCodecs() { 41 | try { 42 | JsonCodingSerializer.builder() 43 | .encodingWith(base = null) 44 | .build() 45 | .serializeValue(emptyList()) 46 | 47 | throw AssertionError("JsonCodingSerializer without any codec provided unexpectedly uses codecs") 48 | } 49 | catch (e: JsonException) { 50 | // good 51 | } 52 | } 53 | 54 | 55 | @Test 56 | fun testDefault() { 57 | anyData.testEncoding(JsonCodingSerializer.default::serializeValue) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/byteData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val byteData: TestData = TestData( 5 | symmetric = mapOf( 6 | Byte.MIN_VALUE to "-128", 7 | (-1).toByte() to "-1", 8 | 0.toByte() to "0", 9 | 1.toByte() to "1", 10 | Byte.MAX_VALUE to "127" 11 | ), 12 | decodableOnly = mapOf( 13 | "-1e20000" to Byte.MIN_VALUE, 14 | "-1000000000000000000000000000000" to Byte.MIN_VALUE, 15 | "-1.0e+2" to (-100).toByte(), 16 | "-1.0e2" to (-100).toByte(), 17 | "-1e+2" to (-100).toByte(), 18 | "-1e2" to (-100).toByte(), 19 | "-100" to (-100).toByte(), 20 | "-1000000000000000000000000000000e-30" to (-1).toByte(), 21 | "-1.9" to (-1).toByte(), 22 | "-1.1" to (-1).toByte(), 23 | "-1E0" to (-1).toByte(), 24 | "-1.0e-2" to 0.toByte(), 25 | "-1e-2" to 0.toByte(), 26 | "-1e-20000" to 0.toByte(), 27 | "-0.000" to 0.toByte(), 28 | "-0" to 0.toByte(), 29 | "0" to 0.toByte(), 30 | "0.000" to 0.toByte(), 31 | "1e-20000" to 0.toByte(), 32 | "1e-2" to 0.toByte(), 33 | "1.0e-2" to 0.toByte(), 34 | "1E0" to 1.toByte(), 35 | "1.1" to 1.toByte(), 36 | "1.9" to 1.toByte(), 37 | "1000000000000000000000000000000e-30" to 1.toByte(), 38 | "100" to 100.toByte(), 39 | "1e2" to 100.toByte(), 40 | "1e+2" to 100.toByte(), 41 | "1.0e2" to 100.toByte(), 42 | "1.0e+2" to 100.toByte(), 43 | "1000000000000000000000000000000" to Byte.MAX_VALUE, 44 | "1e20000" to Byte.MAX_VALUE 45 | ) 46 | ) 47 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/shortData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val shortData: TestData = TestData( 5 | symmetric = mapOf( 6 | Short.MIN_VALUE to "-32768", 7 | (-1).toShort() to "-1", 8 | 0.toShort() to "0", 9 | 1.toShort() to "1", 10 | Short.MAX_VALUE to "32767" 11 | ), 12 | decodableOnly = mapOf( 13 | "-1e20000" to Short.MIN_VALUE, 14 | "-1000000000000000000000000000000" to Short.MIN_VALUE, 15 | "-1.0e+2" to (-100).toShort(), 16 | "-1.0e2" to (-100).toShort(), 17 | "-1e+2" to (-100).toShort(), 18 | "-1e2" to (-100).toShort(), 19 | "-100" to (-100).toShort(), 20 | "-1000000000000000000000000000000e-30" to (-1).toShort(), 21 | "-1.9" to (-1).toShort(), 22 | "-1.1" to (-1).toShort(), 23 | "-1E0" to (-1).toShort(), 24 | "-1.0e-2" to 0.toShort(), 25 | "-1e-2" to 0.toShort(), 26 | "-1e-20000" to 0.toShort(), 27 | "-0.000" to 0.toShort(), 28 | "-0" to 0.toShort(), 29 | "0" to 0.toShort(), 30 | "0.000" to 0.toShort(), 31 | "1e-20000" to 0.toShort(), 32 | "1e-2" to 0.toShort(), 33 | "1.0e-2" to 0.toShort(), 34 | "1E0" to 1.toShort(), 35 | "1.1" to 1.toShort(), 36 | "1.9" to 1.toShort(), 37 | "1000000000000000000000000000000e-30" to 1.toShort(), 38 | "100" to 100.toShort(), 39 | "1e2" to 100.toShort(), 40 | "1e+2" to 100.toShort(), 41 | "1.0e2" to 100.toShort(), 42 | "1.0e+2" to 100.toShort(), 43 | "1000000000000000000000000000000" to Short.MAX_VALUE, 44 | "1e20000" to Short.MAX_VALUE 45 | ) 46 | ) 47 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/basic/NonRecursiveJsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | internal abstract class NonRecursiveJsonDecoderCodec : AbstractJsonDecoderCodec() { 5 | 6 | private val expectedFirstToken = when (decodableType.rawClass) { 7 | Collection::class, 8 | Iterable::class, 9 | List::class, 10 | Sequence::class, 11 | Set::class -> 12 | JsonToken.listStart 13 | 14 | Map::class -> 15 | JsonToken.mapStart 16 | 17 | else -> error("Cannot decode $decodableType") 18 | } 19 | 20 | 21 | @Suppress("UNCHECKED_CAST") 22 | override fun JsonDecoder.decode(valueType: JsonCodingType): Value { 23 | if (nextToken != expectedFirstToken) { 24 | throw JsonException.Schema( 25 | message = "Cannot decode $nextToken as $valueType", 26 | offset = offset, 27 | path = path 28 | ) 29 | } 30 | 31 | val value = JsonParser.default.parseValueOrNull(this, withTermination = false) 32 | 33 | return when { 34 | Sequence::class.java.isAssignableFrom(valueType.rawClass.java) -> (value as Iterable<*>).asSequence() as Value 35 | Set::class.java.isAssignableFrom(valueType.rawClass.java) -> (value as Iterable<*>).toSet() as Value 36 | else -> value as Value 37 | } 38 | } 39 | 40 | 41 | companion object { 42 | 43 | inline fun create(): JsonDecoderCodec = 44 | object : NonRecursiveJsonDecoderCodec() {} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/AbstractJsonEncoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.lang.reflect.* 4 | import kotlin.reflect.* 5 | 6 | 7 | public abstract class AbstractJsonEncoderCodec( 8 | private val additionalProviders: List> = emptyList(), 9 | encodableClass: KClass? = null 10 | ) : JsonEncoderCodec { 11 | 12 | @Suppress("UNCHECKED_CAST") 13 | final override val encodableClass: KClass = encodableClass 14 | ?: JsonCodingType.of((this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments[0]).rawClass as KClass 15 | 16 | 17 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? { 18 | for (provider in additionalProviders) { 19 | val codec = provider.decoderCodecForType(decodableType) 20 | if (codec != null) { 21 | return codec 22 | } 23 | } 24 | 25 | return null 26 | } 27 | 28 | 29 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? { 30 | var codec = super.encoderCodecForClass(encodableClass) 31 | if (codec != null) { 32 | return codec 33 | } 34 | 35 | for (provider in additionalProviders) { 36 | codec = provider.encoderCodecForClass(encodableClass) 37 | if (codec != null) { 38 | return codec 39 | } 40 | } 41 | 42 | return null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/floatData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val floatData: TestData = TestData( 5 | symmetric = mapOf( 6 | -1E38f to "-1.0E38", 7 | -100.999f to "-100.999", 8 | -100.001f to "-100.001", 9 | -1E-38f to "-1.0E-38", 10 | -0.0f to "-0.0", 11 | 0.0f to "0.0", 12 | 1E-38f to "1.0E-38", 13 | 100.001f to "100.001", 14 | 100.999f to "100.999", 15 | 1E38f to "1.0E38" 16 | ), 17 | decodableOnly = mapOf( 18 | "-1e20000" to Float.NEGATIVE_INFINITY, 19 | "-1000000000000000000000000000000" to -1000000000000000000000000000000.0f, 20 | "-9223372036854775809" to -9.223372E18f, // just too large for Long 21 | "-1.0e+2" to -100.0f, 22 | "-1.0e2" to -100.0f, 23 | "-1e+2" to -100.0f, 24 | "-1e2" to -100.0f, 25 | "-100" to -100.0f, 26 | "-1E0" to -1.0f, 27 | "-1.0e-2" to -0.01f, 28 | "-1e-2" to -0.01f, 29 | "-1e-20000" to -0.0f, 30 | "-0.000" to -0.0f, 31 | "-0" to -0.0f, 32 | "0" to 0.0f, 33 | "0.000" to 0.0f, 34 | "1e-20000" to 0.0f, 35 | "1e-2" to 0.01f, 36 | "1.0e-2" to 0.01f, 37 | "1E0" to 1.0f, 38 | "100" to 100.0f, 39 | "1e2" to 100.0f, 40 | "1e+2" to 100.0f, 41 | "1.0e2" to 100.0f, 42 | "1.0e+2" to 100.0f, 43 | "9223372036854775808" to 9.223372E18f, // just too large for Long 44 | "1000000000000000000000000000000" to 1000000000000000000000000000000.0f, 45 | "1e20000" to Float.POSITIVE_INFINITY 46 | ), 47 | nonEncodable = setOf( 48 | Float.NaN, 49 | Float.NEGATIVE_INFINITY, 50 | Float.POSITIVE_INFINITY 51 | ) 52 | ) 53 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/AbstractJsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.lang.reflect.* 4 | import kotlin.reflect.* 5 | 6 | 7 | public abstract class AbstractJsonDecoderCodec( 8 | private val additionalProviders: List> = emptyList(), 9 | decodableType: JsonCodingType? = null 10 | ) : JsonDecoderCodec { 11 | 12 | @Suppress("UNCHECKED_CAST") 13 | final override val decodableType: JsonCodingType = decodableType 14 | ?: JsonCodingType.of((this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments[0]) as JsonCodingType 15 | 16 | 17 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? { 18 | var codec = super.decoderCodecForType(decodableType) 19 | if (codec != null) { 20 | return codec 21 | } 22 | 23 | for (provider in additionalProviders) { 24 | codec = provider.decoderCodecForType(decodableType) 25 | if (codec != null) { 26 | return codec 27 | } 28 | } 29 | 30 | return null 31 | } 32 | 33 | 34 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? { 35 | for (provider in additionalProviders) { 36 | val codec = provider.encoderCodecForClass(encodableClass) 37 | if (codec != null) { 38 | return codec 39 | } 40 | } 41 | 42 | return null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /coding/sources-jvm/codecs/DefaultJsonCodecs.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import io.fluidsonic.json.dynamic.* 4 | 5 | 6 | public object DefaultJsonCodecs { 7 | 8 | public val basic: List> = 9 | listOf( 10 | AnyJsonDecoderCodec, 11 | ArrayJsonCodec, 12 | BooleanArrayJsonCodec, 13 | BooleanJsonCodec, 14 | ByteArrayJsonCodec, 15 | ByteJsonCodec, 16 | CharJsonCodec, 17 | DoubleArrayJsonCodec, 18 | DoubleJsonCodec, 19 | FloatArrayJsonCodec, 20 | FloatJsonCodec, 21 | IntArrayJsonCodec, 22 | IntJsonCodec, 23 | ListJsonDecoderCodec, 24 | LongArrayJsonCodec, 25 | LongJsonCodec, 26 | MapJsonCodec, 27 | SequenceJsonCodec, 28 | SetJsonDecoderCodec, 29 | ShortArrayJsonCodec, 30 | ShortJsonCodec, 31 | StringJsonCodec, 32 | CollectionJsonCodec, // after subclasses 33 | IterableJsonEncoderCodec, // after subclasses 34 | NumberJsonCodec // after subclasses 35 | ) 36 | 37 | 38 | public val extended: List> = 39 | codingImplementationsJava.extendedCodecProviders() 40 | 41 | 42 | public val nonRecursive: List> = 43 | listOf( 44 | ArrayJsonCodec.nonRecursive, 45 | ListJsonDecoderCodec.nonRecursive, 46 | MapJsonCodec.nonRecursive, 47 | SequenceJsonCodec.nonRecursive, 48 | SetJsonDecoderCodec.nonRecursive, 49 | CollectionJsonCodec.nonRecursive, // after subclasses 50 | IterableJsonEncoderCodec.nonRecursive // after subclasses 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /coding/tests-jvm/JsonEncoderTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | import kotlin.test.* 6 | 7 | 8 | class JsonEncoderTest { 9 | 10 | @Test 11 | fun testBuilder() { 12 | StringWriter().let { writer -> 13 | JsonEncoder.builder() 14 | .codecs(BooleanJsonCodec) 15 | .destination(writer) 16 | .build() 17 | .apply { 18 | expect(context).toBe(JsonCodingContext.empty) 19 | writeBoolean(true) 20 | expect(writer.toString()).toBe("true") 21 | } 22 | } 23 | 24 | StringWriter().let { writer -> 25 | JsonEncoder.builder() 26 | .codecs(listOf(BooleanJsonCodec)) 27 | .destination(writer) 28 | .build() 29 | .apply { 30 | expect(context).toBe(JsonCodingContext.empty) 31 | writeBoolean(true) 32 | expect(writer.toString()).toBe("true") 33 | } 34 | } 35 | 36 | val testContext = TestCoderContext() 37 | 38 | StringWriter().let { writer -> 39 | JsonEncoder.builder(testContext) 40 | .codecs() 41 | .destination(JsonWriter.build(writer)) 42 | .build() 43 | .apply { 44 | expect(context).toBe(testContext) 45 | writeBoolean(true) 46 | expect(writer.toString()).toBe("true") 47 | } 48 | } 49 | 50 | StringWriter().let { writer -> 51 | JsonEncoder.builder(testContext) 52 | .codecs() 53 | .destination(writer) 54 | .build() 55 | .apply { 56 | expect(context).toBe(testContext) 57 | writeBoolean(true) 58 | expect(writer.toString()).toBe("true") 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/sources-jvm/0002-CustomProperties.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | 5 | // Note that IntelliJ IDEA still only has limited supported for annotation processing with kapt, so enable this setting first: 6 | // Preferences > Build, Execution, Deployment > Build Tools > Gradle > Runner > Delegate IDE build/run actions to gradle 7 | 8 | // @Json.CustomProperties allows writing custom code to add properties on-the-fly, potentially making use of a context object. 9 | 10 | 11 | fun main() { 12 | val serializer = JsonCodingSerializer 13 | .builder(MyContext(authenticatedUserId = "5678")) 14 | .encodingWith(JsonCodecProvider.generated(MyCodecProvider::class)) 15 | .build() 16 | 17 | println(serializer.serializeValue(listOf( 18 | User(id = "1234", name = "Some Other User", emailAddress = "email@hidden.com"), 19 | User(id = "5678", name = "Authenticated User", emailAddress = "own@email.com") 20 | ))) 21 | } 22 | 23 | 24 | @Json( 25 | decoding = Json.Decoding.none, // prevent decoding altogether 26 | encoding = Json.Encoding.annotatedProperties // only encode properties annotated explicitly 27 | ) 28 | data class User( 29 | @Json.Property val id: String, 30 | @Json.Property val name: String, 31 | val emailAddress: String 32 | ) 33 | 34 | 35 | @Json.CustomProperties // function will be called during encoding 36 | fun JsonEncoder.writeCustomProperties(value: User) { 37 | if (context.authenticatedUserId == value.id) 38 | writeMapElement("emailAddress", value = value.emailAddress) 39 | } 40 | -------------------------------------------------------------------------------- /ktor-serialization/sources-jvm/FluidJsonConverter.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import io.ktor.content.TextContent 4 | import io.ktor.http.* 5 | import io.ktor.http.content.* 6 | import io.ktor.serialization.* 7 | import io.ktor.util.reflect.* 8 | import io.ktor.utils.io.* 9 | import io.ktor.utils.io.charsets.* 10 | import io.ktor.utils.io.jvm.javaio.* 11 | import kotlinx.coroutines.* 12 | 13 | 14 | public class FluidJsonConverter( 15 | private val parser: JsonCodingParser<*> = JsonCodingParser.nonRecursive, 16 | private val serializer: JsonCodingSerializer = JsonCodingSerializer.nonRecursive, 17 | ) : ContentConverter { 18 | 19 | @Suppress("INVISIBLE_MEMBER") 20 | override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? = 21 | withContext(Dispatchers.IO) { 22 | parser.parseValueOfTypeOrNull(content.toInputStream().reader(charset), JsonCodingType.of(typeInfo.reifiedType)) 23 | } 24 | 25 | 26 | override suspend fun serializeNullable(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any?): OutgoingContent = 27 | TextContent(serializer.serializeValue(value), contentType) 28 | } 29 | 30 | 31 | public fun Configuration.fluidJson( 32 | contentType: ContentType = ContentType.Application.Json, 33 | parser: JsonCodingParser<*> = JsonCodingParser.nonRecursive, 34 | serializer: JsonCodingSerializer = JsonCodingSerializer.nonRecursive, 35 | ) { 36 | register( 37 | contentType = contentType, 38 | converter = FluidJsonConverter( 39 | parser = parser, 40 | serializer = serializer, 41 | ), 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /coding/tests-jvm/data/basic/doubleData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | 4 | internal val doubleData: TestData = TestData( 5 | symmetric = mapOf( 6 | -1E200 to "-1.0E200", 7 | -100.999 to "-100.999", 8 | -100.001 to "-100.001", 9 | -1E-200 to "-1.0E-200", 10 | -0.0 to "-0.0", 11 | 0.0 to "0.0", 12 | 1E-200 to "1.0E-200", 13 | 100.001 to "100.001", 14 | 100.999 to "100.999", 15 | 1E200 to "1.0E200" 16 | ), 17 | decodableOnly = mapOf( 18 | "-1e20000" to Double.NEGATIVE_INFINITY, 19 | "-1000000000000000000000000000000" to -1000000000000000000000000000000.0, 20 | "-9223372036854775809" to -9.223372036854776E18, // just too large for Long 21 | "-1.0e+2" to -100.0, 22 | "-1.0e2" to -100.0, 23 | "-1e+2" to -100.0, 24 | "-1e2" to -100.0, 25 | "-100" to -100.0, 26 | "-1.9" to -1.9, 27 | "-1.1" to -1.1, 28 | "-1E0" to -1.0, 29 | "-1.0e-2" to -0.01, 30 | "-1e-2" to -0.01, 31 | "-1e-20000" to -0.0, 32 | "-0.000" to -0.0, 33 | "-0" to -0.0, 34 | "0" to 0.0, 35 | "0.000" to 0.0, 36 | "1e-20000" to 0.0, 37 | "1e-2" to 0.01, 38 | "1.0e-2" to 0.01, 39 | "1E0" to 1.0, 40 | "1.1" to 1.1, 41 | "1.9" to 1.9, 42 | "100" to 100.0, 43 | "1e2" to 100.0, 44 | "1e+2" to 100.0, 45 | "1.0e2" to 100.0, 46 | "1.0e+2" to 100.0, 47 | "9223372036854775808" to 9.223372036854776E18, // just too large for Long 48 | "1000000000000000000000000000000" to 1000000000000000000000000000000.0, 49 | "1e20000" to Double.POSITIVE_INFINITY 50 | ), 51 | nonEncodable = setOf( 52 | Double.NaN, 53 | Double.NEGATIVE_INFINITY, 54 | Double.POSITIVE_INFINITY 55 | ) 56 | ) 57 | -------------------------------------------------------------------------------- /coding/sources-jvm/JsonDecoderCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import kotlin.reflect.* 4 | 5 | 6 | public interface JsonDecoderCodec : JsonCodecProvider { 7 | 8 | public val decodableType: JsonCodingType 9 | 10 | 11 | public fun JsonDecoder.decode(valueType: JsonCodingType): Value 12 | 13 | 14 | @Suppress("UNCHECKED_CAST") 15 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? = 16 | if (this.decodableType.satisfiesType(decodableType)) 17 | this as JsonDecoderCodec 18 | else 19 | null 20 | 21 | 22 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? = 23 | null 24 | 25 | 26 | public companion object { 27 | 28 | private fun JsonCodingType<*>.satisfiesType(requestedType: JsonCodingType<*>): Boolean { 29 | if (isUnconstrainedUpperBoundInGenericContext) { 30 | return true 31 | } 32 | if (requestedType.rawClass != rawClass) { 33 | return false 34 | } 35 | 36 | val decodableArguments = arguments 37 | val requestedArguments = requestedType.arguments 38 | check(decodableArguments.size == requestedArguments.size) 39 | 40 | @Suppress("LoopToCallChain") 41 | for ((index, decodableArgument) in decodableArguments.withIndex()) { 42 | if (!decodableArgument.satisfiesType(requestedArguments[index])) { 43 | return false 44 | } 45 | } 46 | 47 | return true 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/processing/ProcessingResult.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import com.squareup.kotlinpoet.* 4 | import io.fluidsonic.meta.* 5 | 6 | 7 | internal data class ProcessingResult( 8 | val codecs: Collection, 9 | val codecProvider: CodecProvider? 10 | ) { 11 | 12 | internal data class Codec( 13 | val contextType: MQualifiedTypeName, 14 | val decodingStrategy: DecodingStrategy?, 15 | val encodingStrategy: EncodingStrategy?, 16 | val isPublic: Boolean, 17 | val isSingleValue: Boolean, 18 | val name: MQualifiedTypeName, 19 | val valueType: TypeName 20 | ) { 21 | 22 | data class DecodableProperty( 23 | val defaultValue: DefaultValue, 24 | val name: MVariableName, 25 | val serializedName: String, 26 | val type: TypeName, 27 | val typeParameterIndex: Int 28 | ) { 29 | 30 | enum class DefaultValue { 31 | 32 | defaultArgument, 33 | none, 34 | nullReference 35 | } 36 | } 37 | 38 | 39 | data class DecodingStrategy( 40 | val meta: MConstructor, 41 | val properties: Collection 42 | ) 43 | 44 | 45 | data class EncodableProperty( 46 | val importPackageName: MPackageName?, 47 | val name: MVariableName, 48 | val serializedName: String, 49 | val type: TypeName 50 | ) 51 | 52 | 53 | data class EncodingStrategy( 54 | val customPropertyMethods: Collection>, 55 | val properties: Collection 56 | ) 57 | } 58 | 59 | 60 | data class CodecProvider( 61 | val contextType: MTypeReference, 62 | val interfaceType: MTypeReference, 63 | val isPublic: Boolean, 64 | val name: MQualifiedTypeName 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /coding/sources-jvm/implementations/AbstractJsonCodec.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | import java.lang.reflect.* 4 | import kotlin.reflect.* 5 | 6 | 7 | public abstract class AbstractJsonCodec( 8 | private val additionalProviders: List> = emptyList(), 9 | decodableType: JsonCodingType? = null 10 | ) : JsonCodec { 11 | 12 | @Suppress("UNCHECKED_CAST") 13 | final override val decodableType: JsonCodingType = decodableType 14 | ?: JsonCodingType.of((this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments[0]) as JsonCodingType 15 | 16 | final override val encodableClass: KClass = this.decodableType.rawClass 17 | 18 | 19 | override fun decoderCodecForType(decodableType: JsonCodingType): JsonDecoderCodec? { 20 | var codec = super.decoderCodecForType(decodableType) 21 | if (codec != null) { 22 | return codec 23 | } 24 | 25 | for (provider in additionalProviders) { 26 | codec = provider.decoderCodecForType(decodableType) 27 | if (codec != null) { 28 | return codec 29 | } 30 | } 31 | 32 | return null 33 | } 34 | 35 | 36 | override fun encoderCodecForClass(encodableClass: KClass): JsonEncoderCodec? { 37 | var codec = super.encoderCodecForClass(encodableClass) 38 | if (codec != null) { 39 | return codec 40 | } 41 | 42 | for (provider in additionalProviders) { 43 | codec = provider.encoderCodecForClass(encodableClass) 44 | if (codec != null) { 45 | return codec 46 | } 47 | } 48 | 49 | return null 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /coding/tests-jvm/KClassTest.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | import kotlin.test.* 5 | 6 | 7 | class KClassTest { 8 | 9 | @Test 10 | fun testBoxed() { 11 | expect(Boolean::class.boxed).toBe(java.lang.Boolean::class) 12 | expect(Byte::class.boxed).toBe(java.lang.Byte::class) 13 | expect(Char::class.boxed).toBe(java.lang.Character::class) 14 | expect(Double::class.boxed).toBe(java.lang.Double::class) 15 | expect(Float::class.boxed).toBe(java.lang.Float::class) 16 | expect(Int::class.boxed).toBe(java.lang.Integer::class) 17 | expect(Long::class.boxed).toBe(java.lang.Long::class) 18 | expect(Short::class.boxed).toBe(java.lang.Short::class) 19 | expect(String::class.boxed).toBe(String::class) 20 | expect(Void.TYPE.kotlin.boxed).toBe(java.lang.Void::class) 21 | } 22 | 23 | 24 | @Test 25 | fun testIsAssignableOrBoxableFrom() { 26 | expect(String::class.isAssignableOrBoxableFrom(Any::class)) 27 | .toBe(false) 28 | 29 | expect(Any::class.isAssignableOrBoxableFrom(String::class)) 30 | .toBe(true) 31 | 32 | expect(Boolean::class.isAssignableOrBoxableFrom(java.lang.Boolean::class)) 33 | .toBe(true) 34 | 35 | expect(java.lang.Boolean::class.isAssignableOrBoxableFrom(Boolean::class)) 36 | .toBe(true) 37 | } 38 | 39 | 40 | @Test 41 | fun testIsAssignableOrBoxableTo() { 42 | expect(String::class.isAssignableOrBoxableTo(Any::class)) 43 | .toBe(true) 44 | 45 | expect(Any::class.isAssignableOrBoxableTo(String::class)) 46 | .toBe(false) 47 | 48 | expect(Boolean::class.isAssignableOrBoxableTo(java.lang.Boolean::class)) 49 | .toBe(true) 50 | 51 | expect(java.lang.Boolean::class.isAssignableOrBoxableTo(Boolean::class)) 52 | .toBe(true) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /basic/tests-jvm/JsonWriterTest.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | import kotlin.test.* 6 | 7 | 8 | class JsonWriterTest { 9 | 10 | @Test 11 | fun testWithErrorChecking() { 12 | val writer = JsonWriter.build(StringWriter()) 13 | expect(writer.isErrored).toBe(false) 14 | expect(writer.withErrorChecking { "test" }).toBe("test") 15 | expect(writer.isErrored).toBe(false) 16 | 17 | try { 18 | if (0.toBigDecimal() == 0.toBigDecimal()) writer.withErrorChecking { throw RuntimeException() } 19 | throw AssertionError(".withErrorChecking() should not consume exception") 20 | } 21 | catch (e: Throwable) { 22 | expect(writer.isErrored).toBe(true) 23 | } 24 | 25 | try { 26 | writer.withErrorChecking { } 27 | throw AssertionError(".withErrorChecking() throw when errored") 28 | } 29 | catch (e: JsonException) { 30 | expect(writer.isErrored).toBe(true) 31 | } 32 | } 33 | 34 | 35 | @Test 36 | fun testDefaultWriteDelegations() { 37 | var doubleValue: Double? = null 38 | var longValue: Long? = null 39 | var stringValue: String? = null 40 | 41 | val writer = object : DummyJsonWriter() { 42 | 43 | override fun writeDouble(value: Double) { 44 | doubleValue = value 45 | } 46 | 47 | override fun writeLong(value: Long) { 48 | longValue = value 49 | } 50 | 51 | override fun writeString(value: String) { 52 | stringValue = value 53 | } 54 | } 55 | 56 | writer.writeByte(1) 57 | expect(longValue).toBe(1L) 58 | 59 | writer.writeFloat(2.0f) 60 | expect(doubleValue).toBe(2.0) 61 | 62 | writer.writeInt(3) 63 | expect(longValue).toBe(3) 64 | 65 | writer.writeMapKey("4") 66 | expect(stringValue).toBe("4") 67 | 68 | writer.writeShort(5) 69 | expect(longValue).toBe(5) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /annotation-processor/sources-jvm/AnnotationProcessor.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json.annotationprocessor 2 | 3 | import com.google.auto.service.* 4 | import java.io.* 5 | import javax.annotation.processing.* 6 | import javax.lang.model.* 7 | import javax.lang.model.element.* 8 | import javax.tools.* 9 | 10 | 11 | @AutoService(Processor::class) 12 | public class AnnotationProcessor : AbstractProcessor(), ErrorLogger, TypeResolver { 13 | 14 | private val collectionPhase = CollectionPhase( 15 | errorLogger = this, 16 | typeResolver = this 17 | ) 18 | 19 | 20 | override fun getSupportedAnnotationTypes(): Set = 21 | collectionPhase.annotationClasses.mapTo(mutableSetOf()) { it.java.canonicalName } 22 | 23 | 24 | override fun getSupportedSourceVersion(): SourceVersion = 25 | SourceVersion.latestSupported() 26 | 27 | 28 | override fun logError(message: String) { 29 | processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, message) 30 | } 31 | 32 | 33 | override fun process(annotations: Set, roundEnvironment: RoundEnvironment): Boolean { 34 | collectionPhase.collect(roundEnvironment = roundEnvironment) 35 | 36 | if (roundEnvironment.processingOver()) { 37 | val processingResult = ProcessingPhase( 38 | collectionResult = collectionPhase.finish(), 39 | errorLogger = this 40 | ).process() 41 | 42 | GenerationPhase( 43 | outputDirectory = File(processingEnv.options[Options.generatedKotlinFilePath]!!), 44 | processingResult = processingResult 45 | ).generate() 46 | } 47 | 48 | return true 49 | } 50 | 51 | 52 | override fun resolveType(qualifiedName: String): TypeElement? = 53 | processingEnv.elementUtils.getTypeElement(qualifiedName) 54 | 55 | 56 | private object Options { 57 | 58 | const val generatedKotlinFilePath = "kapt.kotlin.generated" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /coding/sources-jvm/utility/KClass.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") 2 | 3 | package io.fluidsonic.json 4 | 5 | import kotlin.Boolean 6 | import kotlin.Byte 7 | import kotlin.Char 8 | import kotlin.Double 9 | import kotlin.Float 10 | import kotlin.Int 11 | import kotlin.Long 12 | import kotlin.Short 13 | import kotlin.reflect.* 14 | import java.lang.Boolean as BoxedBoolean 15 | import java.lang.Byte as BoxedByte 16 | import java.lang.Character as BoxedCharacter 17 | import java.lang.Double as BoxedDouble 18 | import java.lang.Float as BoxedFloat 19 | import java.lang.Integer as BoxedInteger 20 | import java.lang.Long as BoxedLong 21 | import java.lang.Short as BoxedShort 22 | import java.lang.Void as BoxedVoid 23 | 24 | // this will get tricky when adding multi-platform support! 25 | 26 | 27 | internal val KClass<*>.boxed 28 | get() = 29 | if (java.isPrimitive) 30 | when (this) { 31 | Boolean::class -> BoxedBoolean::class 32 | Byte::class -> BoxedByte::class 33 | Char::class -> BoxedCharacter::class 34 | Double::class -> BoxedDouble::class 35 | Float::class -> BoxedFloat::class 36 | Int::class -> BoxedInteger::class 37 | Long::class -> BoxedLong::class 38 | Short::class -> BoxedShort::class 39 | else -> BoxedVoid::class 40 | } 41 | else 42 | this 43 | 44 | 45 | internal fun KClass<*>.isAssignableOrBoxableTo(otherClass: KClass<*>) = 46 | otherClass.isAssignableOrBoxableFrom(this) 47 | 48 | 49 | internal fun KClass<*>.isAssignableOrBoxableFrom(otherClass: KClass<*>) = 50 | boxed.java.isAssignableFrom(otherClass.boxed.java) 51 | 52 | 53 | internal fun KClass.takeIfSubclassOf(superclass: KClass) = 54 | takeIf { isAssignableOrBoxableTo(superclass) } 55 | 56 | 57 | internal fun KClass.takeIfSuperclassOf(subclass: KClass) = 58 | takeIf { subclass.isAssignableOrBoxableTo(this) } 59 | -------------------------------------------------------------------------------- /examples/sources-jvm/0000-Basics.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import io.fluidsonic.json.* 4 | import java.time.* 5 | 6 | // Note that IntelliJ IDEA still only has limited supported for annotation processing with kapt, so enable this setting first: 7 | // Preferences > Build, Execution, Deployment > Build Tools > Gradle > Build an run using: > Gradle 8 | 9 | // EventJsonCodec and AttendeeJsonCodec are generated automatically for the @Json-annotated classes in this file 10 | 11 | fun main() { 12 | val data = Event( 13 | attendees = listOf( 14 | Attendee(emailAddress = "marc@knaup.io", firstName = "Marc", lastName = "Knaup", rsvp = RSVP.going), 15 | Attendee(emailAddress = "john@doe.com", firstName = "John", lastName = "Doe", rsvp = null) 16 | ), 17 | description = "Discussing the fluid-json library.", 18 | end = Instant.now() + Duration.ofHours(2), 19 | id = 1, 20 | start = Instant.now(), 21 | title = "fluid-json MeetUp" 22 | ) 23 | 24 | val serializer = JsonCodingSerializer.builder(MyContext()) // see example 0001 25 | .encodingWith(EventJsonCodec, AttendeeJsonCodec) 26 | .build() 27 | 28 | val serialized = serializer.serializeValue(data) 29 | println("serialized: $serialized") 30 | 31 | val parser = JsonCodingParser.builder(MyContext()) // see example 0001 32 | .decodingWith(EventJsonCodec, AttendeeJsonCodec) 33 | .build() 34 | 35 | val parsed = parser.parseValueOfType(serialized) 36 | println("parsed: $parsed") 37 | } 38 | 39 | 40 | @Json 41 | data class Event( 42 | val attendees: Collection, 43 | val description: String, 44 | val end: Instant, 45 | val id: Int, 46 | val start: Instant, 47 | val title: String 48 | ) 49 | 50 | @Json 51 | data class Attendee( 52 | val emailAddress: String, 53 | val firstName: String, 54 | val lastName: String, 55 | val rsvp: RSVP? 56 | ) 57 | 58 | enum class RSVP { 59 | notGoing, 60 | going 61 | } 62 | -------------------------------------------------------------------------------- /basic/sources-jvm/JsonException.kt: -------------------------------------------------------------------------------- 1 | package io.fluidsonic.json 2 | 3 | 4 | public abstract class JsonException( 5 | message: String, 6 | public val offset: Int = -1, 7 | public val path: JsonPath? = null, 8 | cause: Throwable? = null 9 | ) : RuntimeException(message, cause) { 10 | 11 | override val message: String 12 | get() = buildMessage( 13 | message = super.message ?: "", 14 | offset = offset, 15 | path = path 16 | ) 17 | 18 | 19 | public companion object { 20 | 21 | private fun buildMessage( 22 | message: String, 23 | offset: Int, 24 | path: JsonPath? 25 | ) = buildString { 26 | if (path != null) { 27 | append("at ") 28 | append(path.toString()) 29 | append(": ") 30 | } 31 | 32 | append(message) 33 | 34 | if (offset >= 0) { 35 | append(" - at position ") 36 | append(offset) 37 | } 38 | } 39 | } 40 | 41 | 42 | public class Parsing( 43 | message: String, 44 | offset: Int = -1, 45 | path: JsonPath? = null, 46 | cause: Throwable? = null 47 | ) : JsonException(message = message, offset = offset, path = path, cause = cause) { 48 | 49 | public companion object 50 | } 51 | 52 | 53 | public class Schema( 54 | message: String, 55 | offset: Int = -1, 56 | path: JsonPath? = null, 57 | cause: Throwable? = null 58 | ) : JsonException(message = message, offset = offset, path = path, cause = cause) { 59 | 60 | public companion object 61 | } 62 | 63 | 64 | public class Serialization( 65 | message: String, 66 | path: JsonPath? = null, 67 | cause: Throwable? = null 68 | ) : JsonException(message = message, path = path, cause = cause) { 69 | 70 | public companion object 71 | } 72 | 73 | 74 | public class Syntax( 75 | message: String, 76 | offset: Int = -1, 77 | path: JsonPath? = null, 78 | cause: Throwable? = null 79 | ) : JsonException(message = message, offset = offset, path = path, cause = cause) { 80 | 81 | public companion object 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/sources-jvm/0030-TypeEncoderCodecs.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import examples.EncodingExample.Event 4 | import examples.EncodingExample.EventCodec 5 | import io.fluidsonic.json.* 6 | import java.time.* 7 | 8 | 9 | fun main() { 10 | // Using a codec for encoding specific Kotlin types simplifies JSON serialization a lot 11 | 12 | val serializer = JsonCodingSerializer.builder() 13 | .encodingWith(EventCodec) 14 | .build() 15 | 16 | val json = serializer.serializeValue(listOf( 17 | Event(id = 1, date = Instant.ofEpochSecond(1000000000), title = "One", type = Event.Type.GOOD_NIGHT_OF_SLEEP), 18 | Event(id = 2, date = Instant.ofEpochSecond(2000000000), title = "Two", type = Event.Type.VERY_IMPORTANT_MEETING), 19 | Event(id = 3, date = Instant.ofEpochSecond(3000000000), title = "Three", type = Event.Type.GOOD_NIGHT_OF_SLEEP), 20 | Event(id = 4, date = Instant.ofEpochSecond(4000000000), title = "Four", type = Event.Type.GOOD_NIGHT_OF_SLEEP), 21 | Event(id = 5, date = Instant.ofEpochSecond(5000000000), title = "Five", type = Event.Type.MUM), 22 | Event(id = 6, title = "Six", type = Event.Type.GOOD_NIGHT_OF_SLEEP) 23 | )) 24 | 25 | println(json) 26 | } 27 | 28 | 29 | @Suppress("EnumEntryName") 30 | private object EncodingExample { 31 | 32 | data class Event( 33 | val id: Int, 34 | val date: Instant? = null, 35 | val title: String, 36 | val type: Type 37 | ) { 38 | 39 | enum class Type { 40 | 41 | GOOD_NIGHT_OF_SLEEP, 42 | MUM, 43 | VERY_IMPORTANT_MEETING 44 | } 45 | } 46 | 47 | 48 | object EventCodec : AbstractJsonEncoderCodec() { 49 | 50 | override fun JsonEncoder.encode(value: Event) { 51 | writeIntoMap { 52 | writeMapElement("id", int = value.id) 53 | writeMapElement("date", value = value.date, skipIfNull = true) 54 | writeMapElement("title", string = value.title) 55 | writeMapElement("type", value = value.type) 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /coding-jdk8/tests-jvm/data/TestData.kt: -------------------------------------------------------------------------------- 1 | package tests.coding 2 | 3 | import io.fluidsonic.json.* 4 | 5 | 6 | internal class TestData( 7 | val symmetric: Map = emptyMap(), 8 | val decodableOnly: Map = emptyMap(), 9 | val encodableOnly: Map = emptyMap(), 10 | val nonEncodable: Set = emptySet() 11 | ) { 12 | 13 | fun testDecoding(decode: (input: String) -> Any?) { 14 | for ((expectedOutput, input) in symmetric.toList() + decodableOnly.map { it.value to it.key }) 15 | try { 16 | val output = decode(input) 17 | ?: throw AssertionError("Output is null but expected $expectedOutput (${expectedOutput::class})") 18 | 19 | testEquals(output, expectedOutput) 20 | } 21 | catch (e: Throwable) { 22 | throw AssertionError("${e.message} - when decoding: $input").apply { 23 | stackTrace = e.stackTrace 24 | } 25 | } 26 | } 27 | 28 | 29 | fun testEncoding(encode: (input: Value) -> String) { 30 | for ((input, expectedOutput) in symmetric.toList() + encodableOnly.toList()) 31 | try { 32 | expect(encode(input)).toBe(expectedOutput) 33 | } 34 | catch (e: Throwable) { 35 | throw AssertionError("${e.message} - when encoding ${input::class}: $input").apply { 36 | stackTrace = e.stackTrace 37 | } 38 | } 39 | 40 | for (input in nonEncodable) 41 | try { 42 | encode(input) 43 | throw AssertionError("Encoding succeeded but should have failed when encoding ${input::class}: $input") 44 | } 45 | catch (e: JsonException) { 46 | // good 47 | } 48 | } 49 | 50 | 51 | private fun testEquals(actual: Any, expected: Any) { 52 | val isEqual = expected == actual 53 | if (!isEqual) { 54 | val printableActual = (actual as? Sequence<*>)?.toList() ?: actual 55 | val printableExpected = (expected as? Sequence<*>)?.toList() ?: expected 56 | 57 | throw AssertionError("$printableActual (${actual::class}) should equal $printableExpected (${expected::class})") 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /basic/tests-jvm/StandardWriterRejectTest.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | import kotlin.test.* 6 | 7 | 8 | class StandardWriterRejectTest { 9 | 10 | @Test 11 | fun testTerminate() { 12 | writerShouldFail { terminate() } 13 | writerShouldFail { writeListStart(); terminate() } 14 | writerShouldFail { writeMapStart(); terminate() } 15 | } 16 | 17 | 18 | @Test 19 | fun testWithErrorChecking() { 20 | writerShouldFail { 21 | markAsErrored() 22 | withErrorChecking { } 23 | } 24 | } 25 | 26 | 27 | @Test 28 | fun tesWriteWithNonStringMapKey() { 29 | writerShouldFail { writeMapStart(); writeInt(0) } 30 | writerShouldFail { writeMapStart(); writeMapKey(""); writeNull(); writeInt(0) } 31 | } 32 | 33 | 34 | @Test 35 | fun testWriteAfterEnd() { 36 | writerShouldFail { writeInt(0); writeInt(0) } 37 | } 38 | 39 | 40 | @Test 41 | fun testWriteAfterClose() { 42 | writerShouldFail { writeNull(); close(); writeInt(0) } 43 | } 44 | 45 | 46 | @Test 47 | fun testWriteListEndOutsideList() { 48 | writerShouldFail { writeListEnd() } 49 | } 50 | 51 | 52 | @Test 53 | fun testWriteListEndAfterClose() { 54 | writerShouldFail { writeNull(); close(); writeListEnd() } 55 | } 56 | 57 | 58 | @Test 59 | fun testWriteMapEndAfterKey() { 60 | writerShouldFail { writeMapStart(); writeMapKey(""); writeMapEnd() } 61 | } 62 | 63 | 64 | @Test 65 | fun testWriteMapEndOutsideMap() { 66 | writerShouldFail { writeMapEnd() } 67 | } 68 | 69 | 70 | @Test 71 | fun testWriteMapEndAfterClose() { 72 | writerShouldFail { writeNull(); close(); writeMapEnd() } 73 | } 74 | 75 | 76 | @Test 77 | fun testWriteValueWithUnsupportedType() { 78 | writerShouldFail { writeValue(object {}) } 79 | } 80 | 81 | 82 | private inline fun writerShouldFail(block: JsonWriter.() -> Unit) { 83 | try { 84 | StandardWriter(StringWriter()).block() 85 | throw AssertionError("should fail with a JsonException") 86 | } 87 | catch (e: JsonException) { 88 | // good 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /basic/tests-jvm/JsonTestSuite.kt: -------------------------------------------------------------------------------- 1 | package tests.basic 2 | 3 | import io.fluidsonic.json.* 4 | import java.io.* 5 | import kotlin.test.* 6 | 7 | 8 | class JsonTestSuite { 9 | 10 | private val environments = listOf( 11 | TestEnvironment("JsonParser", JsonParser.default, parserIsRecursive = false) 12 | ) 13 | 14 | 15 | private val nonRecursiveOnlyFileNames = setOf( 16 | "n_structure_100000_opening_arrays.json", 17 | "n_structure_open_array_object.json" 18 | ) 19 | 20 | 21 | @Test 22 | fun test() { 23 | environments.forEach { environment -> 24 | File("../dependencies/JSONTestSuite/test_parsing").listFiles()!! 25 | .sortedBy { it.name } 26 | .filter { it.name.endsWith(".json") } 27 | .filter { !environment.parserIsRecursive || !nonRecursiveOnlyFileNames.contains(it.name) } 28 | .forEach { test(environment = environment, file = it) } 29 | } 30 | } 31 | 32 | 33 | private fun test(environment: TestEnvironment, file: File) { 34 | val result = try { 35 | environment.parser.parseValueOrNull(FileReader(file)) 36 | } 37 | catch (e: JsonException) { 38 | e 39 | } 40 | catch (e: StackOverflowError) { 41 | throw AssertionError("Stack overflow in '${file.name}'") 42 | } 43 | 44 | val actualBehavior = if (result is JsonException) Behavior.rejected else Behavior.accepted 45 | val expectedBehavior = when (file.name.substringBefore('_')) { 46 | "i" -> null 47 | "n" -> Behavior.rejected 48 | "y" -> Behavior.accepted 49 | else -> error("Cannot process file '$file'") 50 | } 51 | 52 | if (expectedBehavior != null && actualBehavior != expectedBehavior) { 53 | var message = "Expected '${file.name}' to be $expectedBehavior but was $actualBehavior" 54 | if (result !is Exception) { 55 | message = "$message: $result" 56 | } 57 | 58 | throw RuntimeException(message, result as? Exception) 59 | } 60 | } 61 | 62 | 63 | private enum class Behavior { 64 | 65 | accepted, 66 | rejected 67 | } 68 | 69 | 70 | private class TestEnvironment( 71 | val name: String, 72 | val parser: JsonParser, 73 | val parserIsRecursive: Boolean 74 | ) 75 | } 76 | --------------------------------------------------------------------------------