diff --git a/Migrating-from-v1.md b/Migrating-from-v1.md index 1fdf5c2b..e3811ae3 100644 --- a/Migrating-from-v1.md +++ b/Migrating-from-v1.md @@ -25,10 +25,63 @@ data class TheDataClass( ) // Now -// ... Nothing, as it is the default behavior! data class TheDataClass( + // ... Nothing, as it is the default behavior! val field: String? ) + +// Or +val avro = Avro { implicitNulls = false } +data class TheDataClass( + @AvroDefault("null") + val field: String? +) +``` + +## Set a field default value to empty array + +```kotlin +// Previously +data class TheDataClass( + @AvroDefault("[]") + val field: List +) + +// Now +data class TheDataClass( + // ... Nothing, as it is the default behavior! + val field: List +) + +// Or +val avro = Avro { implicitEmptyCollections = false } +data class TheDataClass( + @AvroDefault("[]") + val field: List +) +``` + +## Set a field default value to empty map + +```kotlin +// Previously +data class TheDataClass( + @AvroDefault("{}") + val field: Map +) + +// Now +data class TheDataClass( + // ... Nothing, as it is the default behavior! + val field: Map +) + +// Or +val avro = Avro { implicitEmptyCollections = false } +data class TheDataClass( + @AvroDefault("{}") + val field: Map +) ``` ## generic data serialization diff --git a/README.md b/README.md index ecb64d98..f3402ac3 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Here are the main features: - **Encode and decode** anything to and from binary format, and also in generic data :toolbox: - **Generate schemas** based on your values and data classes :pencil: - **Customize** the generated schemas and encoded data with annotations :construction_worker: -- **Fast** as it is reflection-less :rocket: -- **Simple API** to get started quickly, also with native support of `java.time`, `BigDecimal`, `BigInteger` and `UUID` classes :1st_place_medal: +- **Fast** as it is reflection-less :rocket: (check the benchmarks [here](benchmark/README.md#results)) +- **Simple API** to get started quickly, also with native support of java standard classes like `UUID`, `BigDecimal`, `BigInteger` and `java.time` module :1st_place_medal: - **Relaxed matching** for easy schema evolution as it natively [adapts compatible types](#types-matrix) :cyclone: > [!WARNING] @@ -325,6 +325,37 @@ yourAvroInstance.schema() # Usage +## Customizing the configuration + +By default, `Avro` is configured with the following behavior: +- `implicitNulls`: The nullable fields are considered null when decoding if the writer record's schema does not contain this field. +- `implicitEmptyCollections`: The non-nullable map and collection fields are considered empty when decoding if the writer record's schema does not contain this field. + - If `implicitNulls` is true, it takes precedence so the empty collections are set as null if the value is missing instead of an empty collection. +- `validateSerialization`: There is no validation of the schema when encoding or decoding data, which means that serializing using a custom serializer could lead to unexpected behavior. Be careful with your custom serializers. More details [in this section](#set-a-custom-schema). +- `fieldNamingStrategy`: The record's field naming strategy is using the original kotlin field name. To change it, [check this section](#changing-records-field-name). + +So each time you call a method on the `Avro` object implicitely invoke the default configuration. Example: + +```kotlin +Avro.encodeToByteArray(MyData("value")) +Avro.decodeFromByteArray(bytes) +Avro.schema() +``` + +If you need to change the default behavior, you need to create your own instance of `Avro` with the wanted configuration: + +```kotlin +val yourAvroInstance = Avro { + fieldNamingStrategy = FieldNamingStrategy.Builtins.SnakeCase + implicitNulls = false + implicitEmptyCollections = false + validateSerialization = true +} +yourAvroInstance.encodeToByteArray(MyData("value")) +yourAvroInstance.decodeFromByteArray(bytes) +yourAvroInstance.schema() +``` + ## Types matrix | Kotlin type | Generated schema type | Other compatible writer types | Compatible logical type | Note / Serializer class | @@ -529,6 +560,7 @@ There is 3 built-ins strategies: - `NoOp` (default): keeps the original kotlin field name - `SnakeCase`: converts the original kotlin field name to snake_case with underscores before each uppercase letter - `PascalCase`: upper-case the first letter of the original kotlin field name +- If you need more, please [file an issue](https://github.com/avro-kotlin/avro4k/issues/new/choose) First, create your own instance of `Avro` with the wanted naming strategy: @@ -556,9 +588,9 @@ val schema = myCustomizedAvroInstance.schema() // {...,"fields":[{"name" While reading avro binary data, you can miss a field (a kotlin field is present but not in the avro binary data), so Avro4k fails as it is not capable of constructing the kotlin type without the missing field value. -> [!NOTE] -> By default, all nullable fields are optional as a `default: null` is automatically added to the schema ([check this section](#disable-implicit-default-null-for-nullable-fields) -> to opt out from this default behavior). +By default: +- nullable fields are optional and `default: null` is automatically added to the field ([check this section](#disable-implicit-default-null-for-nullable-fields) to opt out from this default behavior). +- nullable fields are optional and `default: null` is automatically added to the field ([check this section](#disable-implicit-default-null-for-nullable-fields) to opt out from this default behavior). ### @AvroDefault @@ -593,7 +625,7 @@ data class MyData( ) ``` -> This impacts only the deserialization of the field, and not the serialization or deserialization. +> This impacts only the deserialization of the field, and not the serialization or the schema generation. ## Add aliases @@ -775,20 +807,6 @@ data class Foo(val a: String, @Transient val b: String = "default value") > [!NOTE] > This impacts the schema generation, the serialization and the deserialization. -## Disable implicit `default: null` for nullable fields - -Avro4k makes by default your nullable fields optional (put `default: null` on all nullable fields if no other explicit default provided). -You can opt out this feature by setting `implicitNulls` to `false` in the `Avro` configuration: - -```kotlin -Avro { - implicitNulls = false -} -``` - -> [!NOTE] -> This impacts the schema generation, the serialization and the deserialization. - ## Force a field to be a `string` type You can force a field (or the value class' property) to have its inferred schema as a `string` type by annotating it with `@AvroString`. diff --git a/api/avro4k-core.api b/api/avro4k-core.api index 9c85b916..dca339c5 100644 --- a/api/avro4k-core.api +++ b/api/avro4k-core.api @@ -27,12 +27,13 @@ public synthetic class com/github/avrokotlin/avro4k/AvroAlias$Impl : com/github/ } public final class com/github/avrokotlin/avro4k/AvroBuilder { - public final fun build ()Lcom/github/avrokotlin/avro4k/AvroConfiguration; public final fun getFieldNamingStrategy ()Lcom/github/avrokotlin/avro4k/FieldNamingStrategy; + public final fun getImplicitEmptyCollections ()Z public final fun getImplicitNulls ()Z public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; public final fun getValidateSerialization ()Z public final fun setFieldNamingStrategy (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;)V + public final fun setImplicitEmptyCollections (Z)V public final fun setImplicitNulls (Z)V public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V public final fun setValidateSerialization (Z)V @@ -40,15 +41,17 @@ public final class com/github/avrokotlin/avro4k/AvroBuilder { public final class com/github/avrokotlin/avro4k/AvroConfiguration { public fun ()V - public fun (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZ)V - public synthetic fun (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZZ)V + public synthetic fun (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/github/avrokotlin/avro4k/FieldNamingStrategy; public final fun component2 ()Z public final fun component3 ()Z - public final fun copy (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZ)Lcom/github/avrokotlin/avro4k/AvroConfiguration; - public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroConfiguration;Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroConfiguration; + public final fun component4 ()Z + public final fun copy (Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZZ)Lcom/github/avrokotlin/avro4k/AvroConfiguration; + public static synthetic fun copy$default (Lcom/github/avrokotlin/avro4k/AvroConfiguration;Lcom/github/avrokotlin/avro4k/FieldNamingStrategy;ZZZILjava/lang/Object;)Lcom/github/avrokotlin/avro4k/AvroConfiguration; public fun equals (Ljava/lang/Object;)Z public final fun getFieldNamingStrategy ()Lcom/github/avrokotlin/avro4k/FieldNamingStrategy; + public final fun getImplicitEmptyCollections ()Z public final fun getImplicitNulls ()Z public final fun getValidateSerialization ()Z public fun hashCode ()I diff --git a/benchmark/README.md b/benchmark/README.md index 54cb7042..d2d2fc73 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -25,15 +25,15 @@ Each benchmark is executed with the following configuration: Computer: Macbook air M2 ``` -Benchmark Mode Cnt Score Error Units Relative Difference (%) -Avro4kBenchmark.read thrpt 5 21443.935 ± 2215.328 ops/s 0.00% -ApacheAvroReflectBenchmark.read thrpt 5 19803.543 ± 485.869 ops/s -7.64% -Avro4kGenericWithApacheAvroBenchmark.read thrpt 5 8836.787 ± 404.874 ops/s -58.79% - -Avro4kBenchmark.write thrpt 5 50565.556 ± 849.344 ops/s 0.00% -ApacheAvroReflectBenchmark.write thrpt 5 46872.768 ± 2406.622 ops/s -7.30% -JacksonAvroBenchmark.write thrpt 5 32349.182 ± 10105.111 ops/s -36.01% -Avro4kGenericWithApacheAvroBenchmark.write thrpt 5 27471.887 ± 315.498 ops/s -45.67% +Benchmark Mode Cnt Score Error Units Relative Difference (%) +Avro4kBenchmark.read thrpt 5 22306.113 ± 208.516 ops/s 0.00% +ApacheAvroReflectBenchmark.read thrpt 5 21048.047 ± 3974.761 ops/s -5.65% +Avro4kGenericWithApacheAvroBenchmark.read thrpt 5 8366.754 ± 975.268 ops/s -62.49% + +Avro4kBenchmark.write thrpt 5 54307.187 ± 789.593 ops/s 0.00% +ApacheAvroReflectBenchmark.write thrpt 5 48056.580 ± 2290.755 ops/s -11.52% +JacksonAvroBenchmark.write thrpt 5 36193.366 ± 1124.036 ops/s -33.34% +Avro4kGenericWithApacheAvroBenchmark.write thrpt 5 28268.377 ± 117.031 ops/s -47.96% ``` > [!WARNING] diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt index 59f2850a..0112f21d 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/Avro.kt @@ -107,14 +107,18 @@ public class AvroBuilder internal constructor(avro: Avro) { @ExperimentalSerializationApi public var implicitNulls: Boolean = avro.configuration.implicitNulls + @ExperimentalSerializationApi + public var implicitEmptyCollections: Boolean = avro.configuration.implicitEmptyCollections + @ExperimentalSerializationApi public var validateSerialization: Boolean = avro.configuration.validateSerialization public var serializersModule: SerializersModule = EmptySerializersModule() - public fun build(): AvroConfiguration = + internal fun build(): AvroConfiguration = AvroConfiguration( fieldNamingStrategy = fieldNamingStrategy, implicitNulls = implicitNulls, + implicitEmptyCollections = implicitEmptyCollections, validateSerialization = validateSerialization ) } diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt index dccfb098..a6029798 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/AvroConfiguration.kt @@ -18,6 +18,15 @@ public data class AvroConfiguration( */ @ExperimentalSerializationApi val implicitNulls: Boolean = true, + /** + * By default, set to `true`, the array & map fields that haven't any default value are set as an empty array or map if the value is missing. It also adds `"default": []` for arrays or `"default": {}` for maps to those fields when generating schema using avro4k. + * + * If `implicitNulls` is true, the empty collections are set as null if the value is missing. + * + * When set to `false`, during decoding, any missing content for an array or a map field without its empty default value is failing. + */ + @ExperimentalSerializationApi + val implicitEmptyCollections: Boolean = true, /** * **To be removed when binary support is stable.** * diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt index ac3bdd45..1614fb47 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/RecordResolver.kt @@ -139,29 +139,35 @@ internal class RecordResolver( if (visited) return@forEachIndexed val readerDefaultAnnotation = classDescriptor.findElementAnnotation(elementIndex) - // TODO try to fallback on the default value of the writer schema field if no readerDefaultAnnotation + val readerField = readerSchema.fields[elementIndex] decodingSteps += if (readerDefaultAnnotation != null) { - val elementSchema = readerSchema.fields[elementIndex].schema() DecodingStep.GetDefaultValue( elementIndex = elementIndex, - schema = elementSchema, - defaultValue = readerDefaultAnnotation.parseValueToGenericData(elementSchema) + schema = readerField.schema(), + defaultValue = readerDefaultAnnotation.parseValueToGenericData(readerField.schema()) ) } else if (classDescriptor.isElementOptional(elementIndex)) { DecodingStep.IgnoreOptionalElement(elementIndex) - } else if (avro.configuration.implicitNulls && - ( - classDescriptor.getElementDescriptor(elementIndex).isNullable || - classDescriptor.getElementDescriptor(elementIndex).isInline && classDescriptor.getElementDescriptor(elementIndex).getElementDescriptor(0).isNullable - ) - ) { + } else if (avro.configuration.implicitNulls && readerField.schema().isNullable) { DecodingStep.GetDefaultValue( elementIndex = elementIndex, - schema = NULL_SCHEMA, + schema = readerField.schema().asSchemaList().first { it.type === Schema.Type.NULL }, defaultValue = null ) + } else if (avro.configuration.implicitEmptyCollections && readerField.schema().isTypeOf(Schema.Type.ARRAY)) { + DecodingStep.GetDefaultValue( + elementIndex = elementIndex, + schema = readerField.schema().asSchemaList().first { it.type === Schema.Type.ARRAY }, + defaultValue = emptyList() + ) + } else if (avro.configuration.implicitEmptyCollections && readerField.schema().isTypeOf(Schema.Type.MAP)) { + DecodingStep.GetDefaultValue( + elementIndex = elementIndex, + schema = readerField.schema().asSchemaList().first { it.type === Schema.Type.MAP }, + defaultValue = emptyMap() + ) } else { DecodingStep.MissingElementValueFailure(elementIndex) } @@ -169,6 +175,10 @@ internal class RecordResolver( return decodingSteps.toTypedArray() } + private fun Schema.isTypeOf(expectedType: Schema.Type): Boolean { + return asSchemaList().any { it.type === expectedType } + } + private fun computeEncodingSteps( classDescriptor: SerialDescriptor, writerSchema: Schema, @@ -265,7 +275,9 @@ internal sealed interface DecodingStep { /** * The element is present in the class descriptor but not in the writer schema, so the default value is used. - * Also, if the [com.github.avrokotlin.avro4k.AvroConfiguration.implicitNulls] is enabled, the default value is `null`. + * Also: + * - if the [com.github.avrokotlin.avro4k.AvroConfiguration.implicitNulls] is enabled, the default value is `null`. + * - if the [com.github.avrokotlin.avro4k.AvroConfiguration.implicitEmptyCollections] is enabled, the default value is an empty array or map. */ data class GetDefaultValue( override val elementIndex: Int, @@ -404,6 +416,4 @@ private fun Schema.resolveUnion( throw SerializationException("Union type does not contain one of ${expectedTypes.asList()}, unable to convert default value '$value' for schema $this") } return types[index] -} - -private val NULL_SCHEMA = Schema.create(Schema.Type.NULL) \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt index 5b5446c7..b5bd2cfe 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/decoder/direct/RecordDirectDecoder.kt @@ -3,6 +3,7 @@ package com.github.avrokotlin.avro4k.internal.decoder.direct import com.github.avrokotlin.avro4k.Avro import com.github.avrokotlin.avro4k.internal.DecodingStep import com.github.avrokotlin.avro4k.internal.decoder.generic.AvroValueGenericDecoder +import com.github.avrokotlin.avro4k.internal.nonNullSerialName import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationException import kotlinx.serialization.builtins.ByteArraySerializer @@ -14,13 +15,13 @@ import org.apache.avro.generic.GenericFixed import org.apache.avro.io.Decoder internal class RecordDirectDecoder( - recordSchema: Schema, + private val writerRecordSchema: Schema, descriptor: SerialDescriptor, avro: Avro, binaryDecoder: org.apache.avro.io.Decoder, ) : AbstractAvroDirectDecoder(avro, binaryDecoder) { // from descriptor element index to schema field. The missing fields are at the end to decode the default values - private val classDescriptor = avro.recordResolver.resolveFields(recordSchema, descriptor) + private val classDescriptor = avro.recordResolver.resolveFields(writerRecordSchema, descriptor) private lateinit var currentDecodingStep: DecodingStep.ValidatedDecodingStep private var nextDecodingStepIndex = 0 @@ -40,7 +41,11 @@ internal class RecordDirectDecoder( is DecodingStep.SkipWriterField -> binaryDecoder.skip(field.schema) is DecodingStep.MissingElementValueFailure -> { - throw SerializationException("No writer schema field matching element index ${field.elementIndex} in descriptor $descriptor") + throw SerializationException( + "Reader field '${descriptor.nonNullSerialName}.${descriptor.getElementName( + field.elementIndex + )}' has no corresponding field in writer schema $writerRecordSchema" + ) } is DecodingStep.DeserializeWriterField -> { diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt index 16f93197..3ecfdd3b 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/helpers.kt @@ -42,6 +42,11 @@ internal val Schema.nullable: Schema } } +internal fun Schema.asSchemaList(): List { + if (!isUnion) return listOf(this) + return types +} + internal fun Schema.isNamedSchema(): Boolean { return this.type == Schema.Type.RECORD || this.type == Schema.Type.ENUM || this.type == Schema.Type.FIXED } @@ -56,8 +61,9 @@ internal fun Schema.isFullNameMatch(fullNameToMatch: String): Boolean { aliases.any { it == fullNameToMatch } } -internal val SerialDescriptor.aliases: Set get() = - findAnnotation()?.value?.toSet() ?: emptySet() +internal val SerialDescriptor.aliases: Set + get() = + findAnnotation()?.value?.toSet() ?: emptySet() private val SCHEMA_PLACEHOLDER = Schema.create(Schema.Type.NULL) diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt index 84e3afa6..db09f93c 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/ClassVisitor.kt @@ -1,14 +1,13 @@ package com.github.avrokotlin.avro4k.internal.schema import com.github.avrokotlin.avro4k.AvroDefault +import com.github.avrokotlin.avro4k.internal.asSchemaList import com.github.avrokotlin.avro4k.internal.isStartingAsJson import com.github.avrokotlin.avro4k.internal.jsonNode import com.github.avrokotlin.avro4k.internal.nonNullSerialName import com.github.avrokotlin.avro4k.serializer.ElementLocation import kotlinx.serialization.SerializationException -import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.nonNullOriginal import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonElement @@ -66,7 +65,6 @@ internal class ClassVisitor( createField( context.avro.configuration.fieldNamingStrategy.resolve(descriptor, elementIndex), FieldAnnotations(descriptor, elementIndex), - descriptor.getElementDescriptor(elementIndex), fieldSchema ) ) @@ -92,17 +90,9 @@ internal class ClassVisitor( private fun createField( fieldName: String, annotations: FieldAnnotations, - elementDescriptor: SerialDescriptor, elementSchema: Schema, ): Schema.Field { - val fieldDefault = getFieldDefault(annotations.default, elementSchema, elementDescriptor) - - val finalSchema = - if (fieldDefault != null) { - reorderUnionIfNeeded(fieldDefault, elementSchema) - } else { - elementSchema - } + val (finalSchema, fieldDefault) = getDefaultAndReorderUnionIfNeeded(annotations, elementSchema) val field = Schema.Field( @@ -116,57 +106,50 @@ internal class ClassVisitor( return field } - /** - * Reorder the union to put the non-null first if the default value is non-null. - */ - private fun reorderUnionIfNeeded( - fieldDefault: Any, - schema: Schema, - ): Schema { - if (schema.isUnion && schema.isNullable) { - var nullNotFirst = false - if (fieldDefault is Collection<*>) { - nullNotFirst = fieldDefault.any { it != JsonProperties.NULL_VALUE } - } else if (fieldDefault != JsonProperties.NULL_VALUE) { - nullNotFirst = true - } - if (nullNotFirst) { - val nullIndex = schema.types.indexOfFirst { it.type == Schema.Type.NULL } - val nonNullTypes = schema.types.toMutableList() - val nullType = nonNullTypes.removeAt(nullIndex) - return Schema.createUnion(nonNullTypes + nullType) + private fun getDefaultAndReorderUnionIfNeeded( + annotations: FieldAnnotations, + elementSchema: Schema, + ): Pair { + val defaultValue = annotations.default?.toAvroObject() + if (defaultValue == null) { + if (context.configuration.implicitNulls && elementSchema.isNullable) { + return elementSchema.moveToHeadOfUnion { it.type == Schema.Type.NULL } to JsonProperties.NULL_VALUE + } else if (context.configuration.implicitEmptyCollections) { + elementSchema.asSchemaList().forEachIndexed { index, schema -> + if (schema.type == Schema.Type.ARRAY) { + return elementSchema.moveToHeadOfUnion(index) to emptyList() + } + if (schema.type == Schema.Type.MAP) { + return elementSchema.moveToHeadOfUnion(index) to emptyMap() + } + } } - } - return schema - } - - private fun getFieldDefault( - default: AvroDefault?, - fieldSchema: Schema, - elementDescriptor: SerialDescriptor, - ): Any? { - val defaultValue = default?.jsonValue - - if (defaultValue == null && context.avro.configuration.implicitNulls && fieldSchema.isNullable) { - return JsonProperties.NULL_VALUE - } else if (defaultValue != null && defaultValue != JsonProperties.NULL_VALUE && elementDescriptor.nonNullOriginal == Char.serializer().descriptor) { - return if (defaultValue is String && defaultValue.length == 1) { - defaultValue.single().code + } else if (defaultValue === JsonProperties.NULL_VALUE) { + // If the user sets "null" but the field is not nullable, maybe the user wanted to set the "null" string default + val finalSchema = elementSchema.moveToHeadOfUnion { it.type == Schema.Type.NULL } + val adaptedDefault = if (!elementSchema.isNullable) "null" else defaultValue + return finalSchema to adaptedDefault + } else if (elementSchema.asSchemaList().any { it.logicalType?.name == CHAR_LOGICAL_TYPE_NAME }) { + // requires a string default value with exactly 1 character, and map the character to the char code as it is an int + if (defaultValue is String && defaultValue.length == 1) { + return elementSchema.moveToHeadOfUnion { it.logicalType?.name == CHAR_LOGICAL_TYPE_NAME } to defaultValue.single().code } else { - throw SerializationException("Default value for Char must be a single character string. Invalid value: $defaultValue") + throw SerializationException("Default value for Char must be a single character string. Invalid value of type ${defaultValue::class.qualifiedName}: $defaultValue") } + } else if (elementSchema.isNullable) { + // default is not null, so let's just put the null schema at the end of the union which should cover the main use cases + return elementSchema.moveToTailOfUnion { it.type === Schema.Type.NULL } to defaultValue } - return defaultValue + return elementSchema to defaultValue } } -private val AvroDefault.jsonValue: Any - get() { - if (value.isStartingAsJson()) { - return Json.parseToJsonElement(value).toAvroObject() - } - return value +private fun AvroDefault.toAvroObject(): Any { + if (value.isStartingAsJson()) { + return Json.parseToJsonElement(value).toAvroObject() } + return value +} private fun JsonElement.toAvroObject(): Any = when (this) { @@ -187,4 +170,42 @@ private fun JsonElement.toAvroObject(): Any = } } } - } \ No newline at end of file + } + +private fun Schema.moveToHeadOfUnion(predicate: (Schema) -> Boolean): Schema { + if (!isUnion) { + return this + } + types.indexOfFirst(predicate).let { index -> + if (index == -1) { + return this + } + return moveToHeadOfUnion(index) + } +} + +private fun Schema.moveToHeadOfUnion(typeIndex: Int): Schema { + if (!isUnion || typeIndex >= types.size) { + return this + } + return Schema.createUnion(types.toMutableList().apply { add(0, removeAt(typeIndex)) }) +} + +private fun Schema.moveToTailOfUnion(predicate: (Schema) -> Boolean): Schema { + if (!isUnion) { + return this + } + types.indexOfFirst(predicate).let { index -> + if (index == -1) { + return this + } + return moveToTailOfUnion(index) + } +} + +private fun Schema.moveToTailOfUnion(typeIndex: Int): Schema { + if (!isUnion || typeIndex >= types.size) { + return this + } + return Schema.createUnion(types.toMutableList().apply { add(removeAt(typeIndex)) }) +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt index c5e0c285..adcf20b9 100644 --- a/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt +++ b/src/main/kotlin/com/github/avrokotlin/avro4k/internal/schema/PolymorphicVisitor.kt @@ -18,7 +18,7 @@ internal class PolymorphicVisitor( override fun endPolymorphicVisit(descriptor: SerialDescriptor) { if (possibleSchemas.isEmpty()) { - throw AvroSchemaGenerationException("Polymorphic descriptor must have at least one possible schema") + throw AvroSchemaGenerationException("Polymorphic descriptor '$descriptor' must have at least one possible schema") } if (possibleSchemas.size == 1) { // flatten the useless union schema diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt index 95a1c4f8..8d0de9af 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/MapEncodingTest.kt @@ -128,7 +128,7 @@ internal class MapEncodingTest : FunSpec({ } .generatesSchema( SchemaBuilder.record("ContextualKeyTests").fields() - .name("map").type(Schema.createMap(Schema.create(Schema.Type.INT))).noDefault() + .name("map").type(Schema.createMap(Schema.create(Schema.Type.INT))).withDefault(mapOf()) .endRecord() ) .isEncodedAs(record(mapOf("a" to 12))) @@ -148,7 +148,11 @@ internal class MapEncodingTest : FunSpec({ ).forEach { keyValue -> test("handle string-able key type: ${keyValue::class.simpleName}") { AvroAssertions.assertThat(GenericMapForTests(mapOf(keyValue to "something")), GenericMapForTests.serializer(keyValue::class.serializer())) - .generatesSchema(Path("/map_string.json")) + .generatesSchema( + SchemaBuilder.record("mapStringStringTest").fields() + .name("map").type(Schema.createMap(Schema.create(Schema.Type.STRING))).withDefault(mapOf()) + .endRecord() + ) .isEncodedAs(record(mapOf(keyValue.toString() to "something"))) } } @@ -166,9 +170,12 @@ internal class MapEncodingTest : FunSpec({ WrappedChar('a') to "a" ).forEach { (keyValue, avroValue) -> test("handle string-able key type inside a value class: ${keyValue::class.simpleName}") { - AvroAssertions.assertThat(GenericMapForTests.serializer(keyValue::class.serializer())) - .generatesSchema(Path("/map_string.json")) AvroAssertions.assertThat(GenericMapForTests(mapOf(keyValue to "something")), GenericMapForTests.serializer(keyValue::class.serializer())) + .generatesSchema( + SchemaBuilder.record("mapStringStringTest").fields() + .name("map").type(Schema.createMap(Schema.create(Schema.Type.STRING))).withDefault(mapOf()) + .endRecord() + ) .isEncodedAs(record(mapOf(avroValue to "something"))) } } diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt index 42d4a047..f07816b2 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/encoding/RecordEncodingTest.kt @@ -33,7 +33,7 @@ internal class RecordEncodingTest : StringSpec({ .name("c").type(Schema.create(Schema.Type.BOOLEAN).nullable).withDefault(null) .name("s").type( SchemaBuilder.record("S").fields() - .name("t").type().array().items().intType().noDefault() + .name("t").type().array().items().intType().arrayDefault(emptyList()) .endRecord() ).noDefault() .name("optionalField").type().intType().noDefault() diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt index ecd904e1..9e302a98 100644 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/AvroDefaultSchemaTest.kt @@ -13,59 +13,57 @@ import org.apache.avro.AvroTypeException import java.math.BigDecimal import kotlin.io.path.Path -internal class AvroDefaultSchemaTest : FunSpec() { - init { - test("schema for data class with @AvroDefault should include default value as a string") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_string.json")) - } - - test("schema for data class with @AvroDefault should include default value as an int") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_int.json")) - } - - test("schema for data class with @AvroDefault should include default value as a float") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_float.json")) - } - - test("schema for data class with @AvroDefault should include default value as a BigDecimal") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_big_decimal.json")) - } - - test("schema for data class with @AvroDefault should include default value as an Enum") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_enum.json")) - } - - test("schema for data class with @AvroDefault should include default value as a list") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_list.json")) - } - - test("schema for data class with @AvroDefault should include default value as a list with a record element type") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_list_of_records.json")) - } - - test("schema for data class with @AvroDefault should include default value as an array") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_array.json")) - } - - test("schema for data class with @AvroDefault should include default value as an set") { - AvroAssertions.assertThat() - .generatesSchema(Path("/avro_default_annotation_set.json")) - } - - test("schema for data class with @AvroDefault should throw error when array type does not match default value type") { - shouldThrow { Avro.schema(BarInvalidArrayType.serializer()) } - shouldThrow { Avro.schema(BarInvalidNonArrayType.serializer()) } - } +internal class AvroDefaultSchemaTest : FunSpec({ + test("schema for data class with @AvroDefault should include default value as a string") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_string.json")) } + test("schema for data class with @AvroDefault should include default value as an int") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_int.json")) + } + + test("schema for data class with @AvroDefault should include default value as a float") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_float.json")) + } + + test("schema for data class with @AvroDefault should include default value as a BigDecimal") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_big_decimal.json")) + } + + test("schema for data class with @AvroDefault should include default value as an Enum") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_enum.json")) + } + + test("schema for data class with @AvroDefault should include default value as a list") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_list.json")) + } + + test("schema for data class with @AvroDefault should include default value as a list with a record element type") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_list_of_records.json")) + } + + test("schema for data class with @AvroDefault should include default value as an array") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_array.json")) + } + + test("schema for data class with @AvroDefault should include default value as an set") { + AvroAssertions.assertThat() + .generatesSchema(Path("/avro_default_annotation_set.json")) + } + + test("schema for data class with @AvroDefault should throw error when array type does not match default value type") { + shouldThrow { Avro.schema(BarInvalidArrayType.serializer()) } + shouldThrow { Avro.schema(BarInvalidNonArrayType.serializer()) } + } +}) { @Serializable private data class BarString( val a: String, diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt new file mode 100644 index 00000000..227f1a5b --- /dev/null +++ b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitConfigSchemaTest.kt @@ -0,0 +1,180 @@ +package com.github.avrokotlin.avro4k.schema + +import com.github.avrokotlin.avro4k.Avro +import com.github.avrokotlin.avro4k.AvroAssertions +import com.github.avrokotlin.avro4k.AvroDefault +import com.github.avrokotlin.avro4k.WrappedInt +import com.github.avrokotlin.avro4k.decodeFromByteArray +import com.github.avrokotlin.avro4k.internal.nullable +import com.github.avrokotlin.avro4k.schema +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import org.apache.avro.Schema +import org.apache.avro.SchemaBuilder + +internal class ImplicitConfigSchemaTest : FunSpec({ + test("Should set default value to null for nullable fields when implicitNulls is true (default)") { + AvroAssertions.assertThat() + .generatesSchema( + SchemaBuilder.record("ImplicitNulls").fields() + .name("string").type(Schema.create(Schema.Type.STRING).nullable).withDefault(null) + .name("boolean").type(Schema.create(Schema.Type.BOOLEAN).nullable).withDefault(null) + .name("booleanWrapped1").type(Schema.create(Schema.Type.BOOLEAN).nullable).withDefault(null) + .name("intWrapped").type(Schema.create(Schema.Type.INT).nullable).withDefault(null) + .name("doubleWrapped").type(Schema.create(Schema.Type.DOUBLE).nullable).withDefault(null) + .name("nested").type( + SchemaBuilder.record("Nested").fields() + .name("string").type(Schema.create(Schema.Type.STRING).nullable).withDefault(null) + .name("boolean").type(Schema.create(Schema.Type.BOOLEAN).nullable).withDefault(null) + .endRecord().nullable + ).withDefault(null) + .name("nullableList").type(Schema.createArray(Schema.create(Schema.Type.STRING)).nullable).withDefault(null) + .name("nullableMap").type(Schema.createMap(Schema.create(Schema.Type.STRING)).nullable).withDefault(null) + .name("stringWithAvroDefault").type().nullable().stringType().stringDefault("implicit nulls bypassed") + .endRecord() + ) + AvroAssertions.assertThat(EmptyType) + .isDecodedAs( + ImplicitNulls( + string = null, + boolean = null, + booleanWrapped1 = NullableBooleanWrapper(null), + intWrapped = null, + doubleWrapped = null, + nested = null, + nullableList = null, + nullableMap = null, + stringWithAvroDefault = "implicit nulls bypassed" + ) + ) + } + + test("Should fail for missing nullable fields when implicitNulls is false. Collections are still having their implicit default") { + AvroAssertions.assertThat() + .withConfig { + implicitNulls = false + } + .generatesSchema( + SchemaBuilder.record("ImplicitNulls").fields() + .name("string").type(Schema.create(Schema.Type.STRING).nullable).noDefault() + .name("boolean").type(Schema.create(Schema.Type.BOOLEAN).nullable).noDefault() + .name("booleanWrapped1").type(Schema.create(Schema.Type.BOOLEAN).nullable).noDefault() + .name("intWrapped").type(Schema.create(Schema.Type.INT).nullable).noDefault() + .name("doubleWrapped").type(Schema.create(Schema.Type.DOUBLE).nullable).noDefault() + .name("nested").type( + SchemaBuilder.record("Nested").fields() + .name("string").type(Schema.create(Schema.Type.STRING).nullable).noDefault() + .name("boolean").type(Schema.create(Schema.Type.BOOLEAN).nullable).noDefault() + .endRecord().nullable + ).noDefault() + .name("nullableList").type().nullable().array().items().stringType().arrayDefault(emptyList()) + .name("nullableMap").type().nullable().map().values().stringType().mapDefault(emptyMap()) + .name("stringWithAvroDefault").type().nullable().stringType().stringDefault("implicit nulls bypassed") + .endRecord() + ) + val avro = + Avro { + implicitNulls = false + } + val bytes = avro.encodeToByteArray(EmptyType) + shouldThrow { + avro.decodeFromByteArray(writerSchema = avro.schema(), bytes) + } + } + + test("Should set default value to empty array for Collection fields when implicitEmptyCollections is true (default)") { + AvroAssertions.assertThat() + .generatesSchema( + SchemaBuilder.record("ImplicitEmptyCollections").fields() + .name("list").type().array().items().stringType().arrayDefault(emptyList()) + .name("set").type().array().items().stringType().arrayDefault(emptyList()) + .name("collection").type().array().items().stringType().arrayDefault(emptyList()) + .name("map").type().map().values().stringType().mapDefault(emptyMap()) + .endRecord() + ) + AvroAssertions.assertThat(EmptyCollectionType) + .isDecodedAs( + ImplicitEmptyCollections( + list = emptyList(), + set = emptySet(), + collection = emptyList(), + map = emptyMap() + ) + ) + } + + test("Should not set default value for Collection fields when implicitEmptyCollections is false") { + AvroAssertions.assertThat() + .withConfig { + implicitEmptyCollections = false + } + .generatesSchema( + SchemaBuilder.record("ImplicitEmptyCollections").fields() + .name("list").type().array().items().stringType().noDefault() + .name("set").type().array().items().stringType().noDefault() + .name("collection").type().array().items().stringType().noDefault() + .name("map").type().map().values().stringType().noDefault() + .endRecord() + ) + val avro = + Avro { + implicitEmptyCollections = false + } + val bytes = avro.encodeToByteArray(EmptyCollectionType) + shouldThrow { + avro.decodeFromByteArray(writerSchema = avro.schema(), bytes) + } + } +}) { + @Serializable + @SerialName("ImplicitNulls") + private data object EmptyType + + @Serializable + @SerialName("ImplicitNulls") + private data class ImplicitNulls( + val string: String?, + val boolean: Boolean?, + val booleanWrapped1: NullableBooleanWrapper, + val intWrapped: WrappedInt?, + val doubleWrapped: NullableDoubleWrapper?, + val nested: Nested?, + val nullableList: List?, + val nullableMap: Map?, + @AvroDefault("implicit nulls bypassed") + val stringWithAvroDefault: String?, + ) + + @Serializable + @SerialName("ImplicitEmptyCollections") + private data object EmptyCollectionType + + @Serializable + @SerialName("ImplicitEmptyCollections") + private data class ImplicitEmptyCollections( + val list: List, + val set: Set, + val collection: Collection, + val map: Map, + ) + + @JvmInline + @Serializable + private value class NullableBooleanWrapper(val value: Boolean?) + + @JvmInline + @Serializable + private value class NullableDoubleWrapper(val value: Double?) + + @Serializable + @SerialName("Nested") + private data class Nested( + val string: String?, + val boolean: Boolean?, + ) +} \ No newline at end of file diff --git a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitNullsSchemaTest.kt b/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitNullsSchemaTest.kt deleted file mode 100644 index 80223714..00000000 --- a/src/test/kotlin/com/github/avrokotlin/avro4k/schema/ImplicitNullsSchemaTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.github.avrokotlin.avro4k.schema - -import com.github.avrokotlin.avro4k.AvroAssertions -import com.github.avrokotlin.avro4k.AvroDefault -import com.github.avrokotlin.avro4k.WrappedInt -import io.kotest.core.spec.style.FunSpec -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlin.io.path.Path - -internal class ImplicitNullsSchemaTest : FunSpec({ - test("Should set default value to null for nullable fields when implicitNulls is true (default)") { - AvroAssertions.assertThat() - .generatesSchema(Path("/nullables-with-defaults.json")) - AvroAssertions.assertThat(EmptyType) - .isDecodedAs( - ImplicitNulls( - string = null, - boolean = null, - booleanWrapped1 = NullableBooleanWrapper(null), - booleanWrapped2 = null, - booleanWrapped3 = null, - nested = null, - stringWithAvroDefault = "implicit nulls bypassed" - ) - ) - } -}) { - @Serializable - @SerialName("ImplicitNulls") - private data object EmptyType - - @Serializable - @SerialName("ImplicitNulls") - private data class ImplicitNulls( - val string: String?, - val boolean: Boolean?, - val booleanWrapped1: NullableBooleanWrapper, - val booleanWrapped2: WrappedInt?, - val booleanWrapped3: NullableDoubleWrapper?, - val nested: Nested?, - @AvroDefault("implicit nulls bypassed") - val stringWithAvroDefault: String?, - ) - - @JvmInline - @Serializable - private value class NullableBooleanWrapper(val value: Boolean?) - - @JvmInline - @Serializable - private value class NullableDoubleWrapper(val value: Double?) - - @Serializable - @SerialName("Nested") - private data class Nested( - val string: String?, - val boolean: Boolean?, - ) -} \ No newline at end of file diff --git a/src/test/resources/array.json b/src/test/resources/array.json index 8ce9d77f..bf2b7f63 100644 --- a/src/test/resources/array.json +++ b/src/test/resources/array.json @@ -10,7 +10,8 @@ "items": { "type": "boolean" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/array_of_maps.json b/src/test/resources/array_of_maps.json index f75deb04..cf8b28db 100644 --- a/src/test/resources/array_of_maps.json +++ b/src/test/resources/array_of_maps.json @@ -10,7 +10,8 @@ "type": "map", "values": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/arrayrecords.json b/src/test/resources/arrayrecords.json index c196827c..9b91fcdc 100644 --- a/src/test/resources/arrayrecords.json +++ b/src/test/resources/arrayrecords.json @@ -17,7 +17,8 @@ } ] } - } + }, + "default": [] } ] } diff --git a/src/test/resources/avro_name_class.json b/src/test/resources/avro_name_class.json deleted file mode 100644 index 23df5609..00000000 --- a/src/test/resources/avro_name_class.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "wibble", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroNameSchemaTest", - "fields": [ - { - "name": "a", - "type": "string" - }, - { - "name": "b", - "type": "string" - } - ] -} diff --git a/src/test/resources/bigdecimal-scale-and-precision.json b/src/test/resources/bigdecimal-scale-and-precision.json deleted file mode 100644 index 52d69e0f..00000000 --- a/src/test/resources/bigdecimal-scale-and-precision.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "BigDecimalPrecisionTest", - "namespace": "com.github.avrokotlin.avro4k.schema.BigDecimalSchemaTest", - "fields": [ - { - "name": "decimal", - "type": { - "type": "bytes", - "logicalType": "decimal", - "precision": 4, - "scale": 1 - } - } - ] -} diff --git a/src/test/resources/bigdecimal.json b/src/test/resources/bigdecimal.json deleted file mode 100644 index c3d61266..00000000 --- a/src/test/resources/bigdecimal.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "BigDecimalTest", - "namespace": "com.github.avrokotlin.avro4k.schema.BigDecimalSchemaTest", - "fields": [ - { - "name": "decimal", - "type": { - "type": "bytes", - "logicalType": "decimal", - "precision": 8, - "scale": 2 - } - } - ] -} diff --git a/src/test/resources/bigdecimal_nullable.json b/src/test/resources/bigdecimal_nullable.json deleted file mode 100644 index 265a0bdf..00000000 --- a/src/test/resources/bigdecimal_nullable.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "record", - "name": "NullableBigDecimalTest", - "namespace": "com.github.avrokotlin.avro4k.schema.BigDecimalSchemaTest", - "fields": [ - { - "name": "decimal", - "type": [ - "null", - { - "type": "bytes", - "logicalType": "decimal", - "precision": 8, - "scale": 2 - } - ] - } - ] -} diff --git a/src/test/resources/bigint.json b/src/test/resources/bigint.json deleted file mode 100644 index d3d405db..00000000 --- a/src/test/resources/bigint.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.BigIntegerSchemaTest", - "fields": [ - { - "name": "b", - "type": "string" - } - ] -} diff --git a/src/test/resources/bigint_nullable.json b/src/test/resources/bigint_nullable.json deleted file mode 100644 index 753368f5..00000000 --- a/src/test/resources/bigint_nullable.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "NullableTest", - "namespace": "com.github.avrokotlin.avro4k.schema.BigIntegerSchemaTest", - "fields": [ - { - "name": "b", - "type": ["null", "string"] - } - ] -} diff --git a/src/test/resources/byte_array.json b/src/test/resources/byte_array.json deleted file mode 100644 index cc70a892..00000000 --- a/src/test/resources/byte_array.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "type": "record", - "name": "ByteArrayTest", - "fields": [ - { - "name": "z", - "type": "bytes" - } - ] -} diff --git a/src/test/resources/bytebuffer.json b/src/test/resources/bytebuffer.json deleted file mode 100644 index 2794eee1..00000000 --- a/src/test/resources/bytebuffer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.ByteArraySchemaTest", - "fields": [ - { - "name": "z", - "type": "bytes" - } - ] -} diff --git a/src/test/resources/class_of_list_of_maps.json b/src/test/resources/class_of_list_of_maps.json index 90229b20..a812ec17 100644 --- a/src/test/resources/class_of_list_of_maps.json +++ b/src/test/resources/class_of_list_of_maps.json @@ -11,7 +11,8 @@ "type": "map", "values": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/contextual_2.json b/src/test/resources/contextual_2.json deleted file mode 100644 index e79dc13b..00000000 --- a/src/test/resources/contextual_2.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.ContextualSchemaTest", - "fields": [ - { - "name": "ts", - "type": { - "type": "long", - "logicalType": "timestamp-micros" - } - }, - { - "name": "withFallback", - "type": ["null", "int"] - } - ] -} diff --git a/src/test/resources/date.json b/src/test/resources/date.json deleted file mode 100644 index 9759ca88..00000000 --- a/src/test/resources/date.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "DateTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "date", - "type": { - "type": "int", - "logicalType": "date" - } - } - ] -} diff --git a/src/test/resources/default.json b/src/test/resources/default.json deleted file mode 100644 index 6f0b0234..00000000 --- a/src/test/resources/default.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "record", - "name": "Foo", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "a", - "type": "string" - }, - { - "name": "b", - "type": "string" - }, - { - "name": "c", - "type": { - "type": "long", - "logicalType": "timestamp-millis" - } - }, - { - "name": "d", - "type": { - "type": "long", - "logicalType": "timestamp-millis" - } - } - ] -} diff --git a/src/test/resources/enum.json b/src/test/resources/enum.json deleted file mode 100644 index e8c3be8c..00000000 --- a/src/test/resources/enum.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "EnumTest", - "namespace": "com.github.avrokotlin.avro4k.schema.EnumSchemaTest", - "fields": [ - { - "name": "wine", - "type": { - "type": "enum", - "name": "Wine", - "namespace": "com.github.avrokotlin.avro4k.schema", - "symbols": ["Malbec", "Shiraz", "CabSav", "Merlot"] - } - } - ] -} diff --git a/src/test/resources/enum_with_default_new_namespace.json b/src/test/resources/enum_with_default_new_namespace.json deleted file mode 100644 index f365f35c..00000000 --- a/src/test/resources/enum_with_default_new_namespace.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "EnumWithDefaultTest", - "namespace": "new", - "fields": [ - { - "name": "type", - "type": { - "type": "enum", - "name": "IngredientType", - "symbols": ["VEGGIE", "MEAT"], - "default": "MEAT" - } - } - ] -} diff --git a/src/test/resources/enum_with_default_value_and_null.json b/src/test/resources/enum_with_default_value_and_null.json deleted file mode 100644 index b6a1f484..00000000 --- a/src/test/resources/enum_with_default_value_and_null.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "type": "record", - "name": "EnumWithAvroDefaultTest", - "namespace": "com.github.avrokotlin.avro4k.schema.EnumSchemaTest", - "fields": [ - { - "name": "type", - "type": [ - "null", - { - "type": "enum", - "name": "IngredientType", - "namespace": "com.github.avrokotlin.avro4k.schema", - "symbols": ["VEGGIE", "MEAT"], - "default": "MEAT" - } - ], - "default": null - } - ] -} diff --git a/src/test/resources/enum_with_documentation.json b/src/test/resources/enum_with_documentation.json deleted file mode 100644 index 4087e6d0..00000000 --- a/src/test/resources/enum_with_documentation.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "record", - "name": "EnumWithDocuTest", - "namespace": "com.github.avrokotlin.avro4k.schema.EnumSchemaTest", - "fields": [ - { - "name": "value", - "type": { - "type": "enum", - "name": "Suit", - "aliases": ["MySuit"], - "doc": "documentation", - "namespace": "com.github.avrokotlin.avro4k.schema", - "symbols": ["SPADES", "HEARTS", "DIAMONDS", "CLUBS"] - } - } - ] -} diff --git a/src/test/resources/fixed_string_top_level_value_type.json b/src/test/resources/fixed_string_top_level_value_type.json deleted file mode 100644 index 82b44757..00000000 --- a/src/test/resources/fixed_string_top_level_value_type.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "fixed", - "name": "FixedValueClass", - "namespace": "com.sksamuel.avro4s.schema", - "size": 8 -} diff --git a/src/test/resources/fixed_string_value_type_as_field.json b/src/test/resources/fixed_string_value_type_as_field.json deleted file mode 100644 index 0853e584..00000000 --- a/src/test/resources/fixed_string_value_type_as_field.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Foo", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroFixedSchemaTest", - "fields": [ - { - "name": "z", - "type": { - "type": "fixed", - "name": "FixedClass", - "size": 8 - } - } - ] -} diff --git a/src/test/resources/inline_type.json b/src/test/resources/inline_type.json deleted file mode 100644 index f8787341..00000000 --- a/src/test/resources/inline_type.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Product", - "namespace": "com.github.avrokotlin.avro4k.schema.InlineClassSchemaTest", - "fields": [ - { - "name": "id", - "type": "string" - }, - { - "name": "name", - "type": "string" - } - ] -} diff --git a/src/test/resources/instant.json b/src/test/resources/instant.json deleted file mode 100644 index 1d7a92f9..00000000 --- a/src/test/resources/instant.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "long", - "logicalType": "timestamp-millis" -} diff --git a/src/test/resources/list.json b/src/test/resources/list.json index 0990901f..e51b7810 100644 --- a/src/test/resources/list.json +++ b/src/test/resources/list.json @@ -10,7 +10,8 @@ "items": { "type": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/list_of_maps.json b/src/test/resources/list_of_maps.json index 222ede7f..2d0c8098 100644 --- a/src/test/resources/list_of_maps.json +++ b/src/test/resources/list_of_maps.json @@ -11,7 +11,8 @@ "type": "map", "values": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/listrecords.json b/src/test/resources/listrecords.json index 64907f99..13edf685 100644 --- a/src/test/resources/listrecords.json +++ b/src/test/resources/listrecords.json @@ -17,7 +17,8 @@ } ] } - } + }, + "default": [] } ] } diff --git a/src/test/resources/local_class_namespace.json b/src/test/resources/local_class_namespace.json deleted file mode 100644 index e5bf7d3f..00000000 --- a/src/test/resources/local_class_namespace.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "NamespaceTestFoo", - "namespace": "com.github.avrokotlin.avro4k.schema.NamespaceSchemaTest", - "fields": [ - { - "name": "inner", - "type": "string" - } - ] -} diff --git a/src/test/resources/localdate.json b/src/test/resources/localdate.json deleted file mode 100644 index d74c8a94..00000000 --- a/src/test/resources/localdate.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "LocalDateTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "date", - "type": { - "type": "int", - "logicalType": "date" - } - } - ] -} diff --git a/src/test/resources/localdate_nullable.json b/src/test/resources/localdate_nullable.json deleted file mode 100644 index c730ccfd..00000000 --- a/src/test/resources/localdate_nullable.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "record", - "name": "NullableLocalDateTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "date", - "type": [ - "null", - { - "type": "int", - "logicalType": "date" - } - ] - } - ] -} diff --git a/src/test/resources/localdatetime.json b/src/test/resources/localdatetime.json deleted file mode 100644 index 3132573a..00000000 --- a/src/test/resources/localdatetime.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "LocalDateTimeTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "time", - "type": { - "type": "long", - "logicalType": "timestamp-millis" - } - } - ] -} diff --git a/src/test/resources/localtime.json b/src/test/resources/localtime.json deleted file mode 100644 index 3e143c4f..00000000 --- a/src/test/resources/localtime.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "LocalTimeTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "time", - "type": { - "type": "int", - "logicalType": "time-millis" - } - } - ] -} diff --git a/src/test/resources/map_boolean_null.json b/src/test/resources/map_boolean_null.json index ac2690d6..5467a7d1 100644 --- a/src/test/resources/map_boolean_null.json +++ b/src/test/resources/map_boolean_null.json @@ -8,7 +8,8 @@ "type": { "type": "map", "values": ["null", "boolean"] - } + }, + "default": {} } ] } diff --git a/src/test/resources/map_int.json b/src/test/resources/map_int.json index 1c0aaff5..60e1f2f6 100644 --- a/src/test/resources/map_int.json +++ b/src/test/resources/map_int.json @@ -8,7 +8,8 @@ "type": { "type": "map", "values": "int" - } + }, + "default": {} } ] } diff --git a/src/test/resources/map_record.json b/src/test/resources/map_record.json index d26ee822..ba003f9a 100644 --- a/src/test/resources/map_record.json +++ b/src/test/resources/map_record.json @@ -17,7 +17,8 @@ } ] } - } + }, + "default": {} } ] } diff --git a/src/test/resources/map_set_nested.json b/src/test/resources/map_set_nested.json index 04122cba..a13814ee 100644 --- a/src/test/resources/map_set_nested.json +++ b/src/test/resources/map_set_nested.json @@ -20,7 +20,8 @@ ] } } - } + }, + "default": {} } ] } diff --git a/src/test/resources/map_string.json b/src/test/resources/map_string.json deleted file mode 100644 index e336861a..00000000 --- a/src/test/resources/map_string.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "type": "record", - "name": "mapStringStringTest", - "fields": [ - { - "name": "map", - "type": { - "type": "map", - "values": "string" - } - } - ] -} diff --git a/src/test/resources/mixed_polymorphic_nullable_referenced.json b/src/test/resources/mixed_polymorphic_nullable_referenced.json deleted file mode 100644 index 8c746726..00000000 --- a/src/test/resources/mixed_polymorphic_nullable_referenced.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "type": "record", - "name": "ReferencingNullableMixedPolymorphic", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "nullable", - "type": [ - "null", - { - "type": "record", - "name": "AddExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "ConstExpr", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "MultiplicationExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NegateExpr", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NullaryExpr", - "fields": [] - }, - { - "type": "record", - "name": "SubstractExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - } - ], - "default": null - } - ] -} diff --git a/src/test/resources/mixed_polymorphic_referenced.json b/src/test/resources/mixed_polymorphic_referenced.json deleted file mode 100644 index 803e5734..00000000 --- a/src/test/resources/mixed_polymorphic_referenced.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "type": "record", - "name": "ReferencingMixedPolymorphic", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "notNullable", - "type": [ - { - "type": "record", - "name": "AddExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "ConstExpr", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "MultiplicationExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NegateExpr", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NullaryExpr", - "fields": [] - }, - { - "type": "record", - "name": "SubstractExpr", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - } - ] - } - ] -} diff --git a/src/test/resources/namespace.json b/src/test/resources/namespace.json deleted file mode 100644 index 5d35ece0..00000000 --- a/src/test/resources/namespace.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "type": "record", - "name": "AnnotatedNamespace", - "namespace": "com.yuval", - "fields": [ - { - "name": "s", - "type": "string" - }, - { - "name": "internal", - "type": { - "type": "record", - "name": "InternalAnnotated", - "namespace": "com.yuval.internal", - "fields": [ - { - "name": "i", - "type": "int" - } - ] - } - } - ] -} diff --git a/src/test/resources/namespace_empty.json b/src/test/resources/namespace_empty.json deleted file mode 100644 index 48a0de6f..00000000 --- a/src/test/resources/namespace_empty.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "type": "record", - "name": "Foo", - "fields": [ - { - "name": "s", - "type": "string" - } - ] -} diff --git a/src/test/resources/namespace_nested.json b/src/test/resources/namespace_nested.json deleted file mode 100644 index 5636d3b0..00000000 --- a/src/test/resources/namespace_nested.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "type": "record", - "name": "NestedValueClasses", - "namespace": "toto", - "fields": [ - { - "name": "nested", - "type": { - "type": "record", - "name": "Nested3", - "namespace": "primaryspace", - "fields": [ - { - "name": "s", - "type": "string" - }, - { - "name": "enum", - "type": { - "type": "enum", - "name": "NestedEnum", - "symbols": ["A", "B"], - "default": "B" - } - }, - { - "name": "fixed", - "type": { - "type": "fixed", - "name": "fixed", - "size": 42 - } - } - ] - } - } - ] -} diff --git a/src/test/resources/nullable_enum.json b/src/test/resources/nullable_enum.json deleted file mode 100644 index adcbcbcf..00000000 --- a/src/test/resources/nullable_enum.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.sksamuel.avro4s.schema.EnumSchemaTest", - "fields": [ - { - "name": "wine", - "type": { - "type": "enum", - "name": "Wine", - "namespace": "com.sksamuel.avro4s.schema", - "symbols": ["Malbec", "Shiraz", "CabSav", "Merlot"] - } - } - ] -} diff --git a/src/test/resources/nullables-with-defaults.json b/src/test/resources/nullables-with-defaults.json deleted file mode 100644 index df90c688..00000000 --- a/src/test/resources/nullables-with-defaults.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "type": "record", - "name": "ImplicitNulls", - "fields": [ - { - "name": "string", - "type": ["null", "string"], - "default": null - }, - { - "name": "boolean", - "type": ["null", "boolean"], - "default": null - }, - { - "name": "booleanWrapped1", - "type": ["null", "boolean"], - "default": null - }, - { - "name": "booleanWrapped2", - "type": ["null", "int"], - "default": null - }, - { - "name": "booleanWrapped3", - "type": ["null", "double"], - "default": null - }, - { - "name": "nested", - "type": [ - "null", - { - "type": "record", - "name": "Nested", - "fields": [ - { - "name": "string", - "type": ["null", "string"], - "default": null - }, - { - "name": "boolean", - "type": ["null", "boolean"], - "default": null - } - ] - } - ], - "default": null - }, - { - "name": "stringWithAvroDefault", - "type": ["string", "null"], - "default": "implicit nulls bypassed" - } - ] -} diff --git a/src/test/resources/nullables.json b/src/test/resources/nullables.json deleted file mode 100644 index abc81085..00000000 --- a/src/test/resources/nullables.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.NullableSchemaTest", - "fields": [ - { - "name": "nullableString", - "type": ["null", "string"] - }, - { - "name": "nullableBoolean", - "type": ["null", "boolean"] - } - ] -} diff --git a/src/test/resources/polymorphic.json b/src/test/resources/polymorphic.json deleted file mode 100644 index b5d718ad..00000000 --- a/src/test/resources/polymorphic.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "type": "record", - "name": "UnsealedChildOne", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "one", - "type": "string" - } - ] - }, - { - "type": "record", - "name": "UnsealedChildTwo", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "two", - "type": "string" - } - ] - } -] diff --git a/src/test/resources/polymorphic_reference.json b/src/test/resources/polymorphic_reference.json deleted file mode 100644 index 0b508b0d..00000000 --- a/src/test/resources/polymorphic_reference.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "type": "record", - "name": "ReferencingPolymorphicRoot", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "root", - "type": [ - { - "type": "record", - "name": "UnsealedChildOne", - "fields": [ - { - "name": "one", - "type": "string" - } - ] - }, - { - "type": "record", - "name": "UnsealedChildTwo", - "fields": [ - { - "name": "two", - "type": "string" - } - ] - } - ] - }, - { - "name": "nullableRoot", - "type": ["null", "UnsealedChildOne", "UnsealedChildTwo"] - } - ] -} diff --git a/src/test/resources/polymorphic_reference_list.json b/src/test/resources/polymorphic_reference_list.json deleted file mode 100644 index 61983e30..00000000 --- a/src/test/resources/polymorphic_reference_list.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "type": "record", - "name": "PolymorphicRootInList", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "listOfRoot", - "type": { - "type": "array", - "items": [ - { - "type": "record", - "name": "UnsealedChildOne", - "fields": [ - { - "name": "one", - "type": "string" - } - ] - }, - { - "type": "record", - "name": "UnsealedChildTwo", - "fields": [ - { - "name": "two", - "type": "string" - } - ] - } - ] - } - } - ] -} diff --git a/src/test/resources/polymorphic_reference_map.json b/src/test/resources/polymorphic_reference_map.json deleted file mode 100644 index 1fd5828e..00000000 --- a/src/test/resources/polymorphic_reference_map.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "type": "record", - "name": "PolymorphicRootInMap", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "mapOfRoot", - "type": { - "type": "map", - "values": [ - { - "type": "record", - "name": "UnsealedChildOne", - "fields": [ - { - "name": "one", - "type": "string" - } - ] - }, - { - "type": "record", - "name": "UnsealedChildTwo", - "fields": [ - { - "name": "two", - "type": "string" - } - ] - } - ] - } - } - ] -} diff --git a/src/test/resources/primitive_boolean.json b/src/test/resources/primitive_boolean.json deleted file mode 100644 index 012160df..00000000 --- a/src/test/resources/primitive_boolean.json +++ /dev/null @@ -1 +0,0 @@ -"boolean" diff --git a/src/test/resources/primitive_bytes.json b/src/test/resources/primitive_bytes.json deleted file mode 100644 index 55393ab6..00000000 --- a/src/test/resources/primitive_bytes.json +++ /dev/null @@ -1 +0,0 @@ -"bytes" diff --git a/src/test/resources/primitive_double.json b/src/test/resources/primitive_double.json deleted file mode 100644 index 65846786..00000000 --- a/src/test/resources/primitive_double.json +++ /dev/null @@ -1 +0,0 @@ -"double" diff --git a/src/test/resources/primitive_float.json b/src/test/resources/primitive_float.json deleted file mode 100644 index b2069c41..00000000 --- a/src/test/resources/primitive_float.json +++ /dev/null @@ -1 +0,0 @@ -"float" diff --git a/src/test/resources/primitive_int.json b/src/test/resources/primitive_int.json deleted file mode 100644 index a21274f5..00000000 --- a/src/test/resources/primitive_int.json +++ /dev/null @@ -1 +0,0 @@ -"int" diff --git a/src/test/resources/primitive_long.json b/src/test/resources/primitive_long.json deleted file mode 100644 index a1194d4e..00000000 --- a/src/test/resources/primitive_long.json +++ /dev/null @@ -1 +0,0 @@ -"long" diff --git a/src/test/resources/primitive_string.json b/src/test/resources/primitive_string.json deleted file mode 100644 index ace2d72d..00000000 --- a/src/test/resources/primitive_string.json +++ /dev/null @@ -1 +0,0 @@ -"string" diff --git a/src/test/resources/props_annotation_class.json b/src/test/resources/props_annotation_class.json deleted file mode 100644 index 06abad6a..00000000 --- a/src/test/resources/props_annotation_class.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "record", - "name": "TypeAnnotated", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroPropSchemaTest", - "fields": [ - { - "name": "str", - "type": "string" - } - ], - "cold": "play" -} diff --git a/src/test/resources/props_annotation_field.json b/src/test/resources/props_annotation_field.json deleted file mode 100644 index 0fb57646..00000000 --- a/src/test/resources/props_annotation_field.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "type": "record", - "name": "AnnotatedProperties", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroPropSchemaTest", - "fields": [ - { - "name": "str", - "type": "string", - "cold": "play" - }, - { - "name": "long", - "type": "long", - "kate": "bush" - }, - { - "name": "int", - "type": "int" - } - ] -} diff --git a/src/test/resources/props_annotation_scala_enum.json b/src/test/resources/props_annotation_scala_enum.json deleted file mode 100644 index 15316557..00000000 --- a/src/test/resources/props_annotation_scala_enum.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "record", - "name": "EnumAnnotated", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroPropSchemaTest", - "fields": [ - { - "name": "colours", - "type": { - "type": "enum", - "name": "Colours", - "symbols": ["Red", "Green", "Blue"] - }, - "cold": "play" - } - ] -} diff --git a/src/test/resources/props_json_annotation_field.json b/src/test/resources/props_json_annotation_field.json deleted file mode 100644 index de13ab73..00000000 --- a/src/test/resources/props_json_annotation_field.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "record", - "name": "AnnotatedProperties", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroJsonPropSchemaTest", - "fields": [ - { - "name": "str", - "type": "string", - "guns": ["and", "roses"] - }, - { - "name": "long", - "type": "long", - "jean": ["michel", "jarre"] - }, - { - "name": "int", - "type": "int", - "object": { - "a": "foo", - "b": 200, - "c": true, - "d": null, - "e": { "e1": null, "e2": 429 }, - "f": ["bar", 404, false, null, {}] - } - } - ] -} diff --git a/src/test/resources/props_json_annotation_scala_enum.json b/src/test/resources/props_json_annotation_scala_enum.json deleted file mode 100644 index 33fa29b8..00000000 --- a/src/test/resources/props_json_annotation_scala_enum.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "enum", - "name": "Colours", - "symbols": ["Red", "Green", "Blue"], - "guns": ["and", "roses"] -} diff --git a/src/test/resources/sealed_interface_hierarchy_referenced.json b/src/test/resources/sealed_interface_hierarchy_referenced.json deleted file mode 100644 index 9aa77529..00000000 --- a/src/test/resources/sealed_interface_hierarchy_referenced.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "type": "record", - "name": "ReferencingSealedInterface", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "notNullable", - "type": [ - { - "type": "record", - "name": "AddCalculable", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NegateCalculable", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NullaryCalculable", - "fields": [] - }, - { - "type": "record", - "name": "SubstractCalculable", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - } - ] - } - ] -} diff --git a/src/test/resources/sealed_interface_nullable_referenced.json b/src/test/resources/sealed_interface_nullable_referenced.json deleted file mode 100644 index 967ccc3c..00000000 --- a/src/test/resources/sealed_interface_nullable_referenced.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "type": "record", - "name": "ReferencingNullableSealedInterface", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "nullable", - "type": [ - "null", - { - "type": "record", - "name": "AddCalculable", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NegateCalculable", - "fields": [ - { - "name": "value", - "type": "int" - } - ] - }, - { - "type": "record", - "name": "NullaryCalculable", - "fields": [] - }, - { - "type": "record", - "name": "SubstractCalculable", - "fields": [ - { - "name": "left", - "type": "int" - }, - { - "name": "right", - "type": "int" - } - ] - } - ], - "default": null - } - ] -} diff --git a/src/test/resources/set_of_maps.json b/src/test/resources/set_of_maps.json index 6dc91890..e657d999 100644 --- a/src/test/resources/set_of_maps.json +++ b/src/test/resources/set_of_maps.json @@ -11,7 +11,8 @@ "type": "map", "values": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/setdoubles.json b/src/test/resources/setdoubles.json index 962b1fb6..da4e979a 100644 --- a/src/test/resources/setdoubles.json +++ b/src/test/resources/setdoubles.json @@ -10,7 +10,8 @@ "items": { "type": "double" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/setrecords.json b/src/test/resources/setrecords.json index 0bc04264..9016f3d2 100644 --- a/src/test/resources/setrecords.json +++ b/src/test/resources/setrecords.json @@ -17,7 +17,8 @@ } ] } - } + }, + "default": [] } ] } diff --git a/src/test/resources/setstrings.json b/src/test/resources/setstrings.json index 11bb9e8f..2c32d345 100644 --- a/src/test/resources/setstrings.json +++ b/src/test/resources/setstrings.json @@ -10,7 +10,8 @@ "items": { "type": "string" } - } + }, + "default": [] } ] } diff --git a/src/test/resources/timestamp.json b/src/test/resources/timestamp.json deleted file mode 100644 index 3bad67a2..00000000 --- a/src/test/resources/timestamp.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "TimestampTest", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "ts", - "type": { - "type": "long", - "logicalType": "timestamp-millis" - } - } - ] -} diff --git a/src/test/resources/timestamp_nullable.json b/src/test/resources/timestamp_nullable.json deleted file mode 100644 index 1447a883..00000000 --- a/src/test/resources/timestamp_nullable.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.DateSchemaTest", - "fields": [ - { - "name": "ts", - "type": [ - "null", - { - "type": "long", - "logicalType": "timestamp-millis" - } - ] - } - ] -} diff --git a/src/test/resources/top_level_class_namespace.json b/src/test/resources/top_level_class_namespace.json deleted file mode 100644 index 38d2f564..00000000 --- a/src/test/resources/top_level_class_namespace.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Tau", - "namespace": "com.github.avrokotlin.avro4k.schema", - "fields": [ - { - "name": "a", - "type": "string" - }, - { - "name": "b", - "type": "boolean" - } - ] -} diff --git a/src/test/resources/top_level_object_namespace.json b/src/test/resources/top_level_object_namespace.json deleted file mode 100644 index 5190f909..00000000 --- a/src/test/resources/top_level_object_namespace.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Inner", - "namespace": "com.github.avrokotlin.avro4k.schema.Outer", - "fields": [ - { - "name": "a", - "type": "string" - }, - { - "name": "b", - "type": "boolean" - } - ] -} diff --git a/src/test/resources/transient.json b/src/test/resources/transient.json deleted file mode 100644 index 81ec5dd4..00000000 --- a/src/test/resources/transient.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "TransientFoo", - "namespace": "com.github.avrokotlin.avro4k.schema.TransientSchemaTest", - "fields": [ - { - "name": "a", - "type": "string" - }, - { - "name": "c", - "type": "string" - } - ] -} diff --git a/src/test/resources/url.json b/src/test/resources/url.json deleted file mode 100644 index c386323e..00000000 --- a/src/test/resources/url.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "Test", - "namespace": "com.github.avrokotlin.avro4k.schema.URLSchemaTest", - "fields": [ - { - "name": "b", - "type": "string" - } - ] -} diff --git a/src/test/resources/url_nullable.json b/src/test/resources/url_nullable.json deleted file mode 100644 index f9c5ab02..00000000 --- a/src/test/resources/url_nullable.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "record", - "name": "NullableTest", - "namespace": "com.github.avrokotlin.avro4k.schema.URLSchemaTest", - "fields": [ - { - "name": "b", - "type": ["null", "string"] - } - ] -} diff --git a/src/test/resources/uuid.json b/src/test/resources/uuid.json deleted file mode 100644 index 5324e320..00000000 --- a/src/test/resources/uuid.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "record", - "name": "UUIDTest", - "namespace": "com.github.avrokotlin.avro4k.schema.UUIDSchemaTest", - "fields": [ - { - "name": "uuid", - "type": { - "type": "string", - "logicalType": "uuid" - } - } - ] -} diff --git a/src/test/resources/value_class.json b/src/test/resources/value_class.json deleted file mode 100644 index 866977f4..00000000 --- a/src/test/resources/value_class.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "record", - "name": "ContainsInlineTest", - "namespace": "com.github.avrokotlin.avro4k.schema.ValueClassSchemaTest", - "fields": [ - { - "name": "id", - "type": "string" - }, - { - "name": "uuid", - "type": { - "type": "string", - "logicalType": "uuid" - } - } - ] -} diff --git a/src/test/resources/value_type.json b/src/test/resources/value_type.json deleted file mode 100644 index 048b03af..00000000 --- a/src/test/resources/value_type.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "record", - "name": "Product", - "namespace": "com.github.avrokotlin.avro4k.schema.AvroInlineSchemaTest", - "fields": [ - { - "name": "id", - "type": "string" - }, - { - "name": "name", - "type": "string" - } - ] -}