Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce PolymorphismStyle.None #494

Merged
merged 7 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 35,7 @@ package com.charleskorn.kaml
* * [sequenceBlockIndent]: number of spaces to use as indentation for sequences, if [sequenceStyle] set to [SequenceStyle.Block]
* * [allowAnchorsAndAliases]: set to true to allow anchors and aliases when decoding YAML (defaults to `false`)
*/
public data class YamlConfiguration constructor(
public data class YamlConfiguration(
charleskorn marked this conversation as resolved.
Show resolved Hide resolved
internal val encodeDefaults: Boolean = true,
internal val strictMode: Boolean = true,
internal val extensionDefinitionPrefix: String? = null,
Expand All @@ -55,6 55,7 @@ public data class YamlConfiguration constructor(
public enum class PolymorphismStyle {
Tag,
Property,
None,
}

public enum class SequenceStyle {
Expand Down
9 changes: 8 additions & 1 deletion src/commonMain/kotlin/com/charleskorn/kaml/YamlInput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 63,21 @@ public sealed class YamlInput(
is StructureKind.MAP -> YamlMapInput(node, yaml, context, configuration)
is SerialKind.CONTEXTUAL -> YamlContextualInput(node, yaml, context, configuration)
is PolymorphicKind -> when (configuration.polymorphismStyle) {
PolymorphismStyle.None ->
throw IncorrectTypeException("Encountered a polymorphic map descriptor but PolymorphismStyle is 'None'", node.path)
PolymorphismStyle.Tag -> throw MissingTypeTagException(node.path)
PolymorphismStyle.Property -> createPolymorphicMapDeserializer(node, yaml, context, configuration)
}
else -> throw IncorrectTypeException("Expected ${descriptor.kind.friendlyDescription}, but got a map", node.path)
}

is YamlTaggedNode -> when {
descriptor.kind is PolymorphicKind && configuration.polymorphismStyle == PolymorphismStyle.Tag -> YamlPolymorphicInput(node.tag, node.path, node.innerNode, yaml, context, configuration)
descriptor.kind is PolymorphicKind && configuration.polymorphismStyle == PolymorphismStyle.None -> {
throw IncorrectTypeException("Encountered a tagged polymorphic descriptor but PolymorphismStyle is 'None'", node.path)
}
descriptor.kind is PolymorphicKind && configuration.polymorphismStyle == PolymorphismStyle.Tag -> {
YamlPolymorphicInput(node.tag, node.path, node.innerNode, yaml, context, configuration)
}
else -> createFor(node.innerNode, yaml, context, configuration, descriptor)
}
}
Expand Down
43 changes: 43 additions & 0 deletions src/commonTest/kotlin/com/charleskorn/kaml/YamlReadingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2029,6 2029,49 @@ class YamlReadingTest : DescribeSpec({
}
}
}

describe("given polymorphic inputs when PolymorphismStyle.None is used") {
val polymorphicYaml = Yaml(serializersModule = polymorphicModule, configuration = YamlConfiguration(polymorphismStyle = PolymorphismStyle.None))

context("given tagged input") {
val input = """
!<sealedString>
value: "asdfg"
""".trimIndent()

context("parsing that input") {
it("throws an appropriate exception") {
val exception = shouldThrow<IncorrectTypeException> { polymorphicYaml.decodeFromString(TestSealedStructure.serializer(), input) }

exception.asClue {
it.message shouldBe "Encountered a tagged polymorphic descriptor but PolymorphismStyle is 'None'"
it.line shouldBe 1
it.column shouldBe 1
it.path shouldBe YamlPath.root
}
}
}
}
context("given property polymorphism input") {
val input = """
type: sealedString
value: "asdfg"
""".trimIndent()

context("parsing that input") {
it("throws an appropriate exception") {
val exception = shouldThrow<IncorrectTypeException> { polymorphicYaml.decodeFromString(TestSealedStructure.serializer(), input) }

exception.asClue {
it.message shouldBe "Encountered a polymorphic map descriptor but PolymorphismStyle is 'None'"
it.line shouldBe 1
it.column shouldBe 1
it.path shouldBe YamlPath.root
}
}
}
}
}
}

describe("parsing values with a dynamically installed serializer") {
Expand Down
3 changes: 3 additions & 0 deletions src/jsMain/kotlin/com/charleskorn/kaml/YamlOutput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 130,9 @@ internal class YamlOutput(
emitQuotedScalar(typeName, SingleLineStringStyle.DoubleQuoted.scalarStyle)
}
}
PolymorphismStyle.None -> {
emitter.emit(MappingStartEvent(null, null, true, FlowStyle.BLOCK))
}
}
}
else -> {
Expand Down
3 changes: 3 additions & 0 deletions src/jvmMain/kotlin/com/charleskorn/kaml/YamlOutput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 131,9 @@ internal class YamlOutput(
emitQuotedScalar(typeName.get(), SingleLineStringStyle.DoubleQuoted.scalarStyle)
}
}
PolymorphismStyle.None -> {
emitter.emit(MappingStartEvent(Optional.empty(), Optional.empty(), true, FlowStyle.BLOCK))
}
}
}
else -> {
Expand Down
75 changes: 75 additions & 0 deletions src/jvmTest/kotlin/com/charleskorn/kaml/JvmYamlWritingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 20,7 @@ package com.charleskorn.kaml

import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.serializer
import java.io.ByteArrayOutputStream

Expand Down Expand Up @@ -103,6 104,68 @@ class JvmYamlWritingTest : DescribeSpec({
""".trimMargin()
}
}

it("should support polymorphic writing with property") {
with(
Yaml(
configuration = YamlConfiguration(
polymorphismStyle = PolymorphismStyle.Property,
),
),
) {
val output = ByteArrayOutputStream()
encodeToStream(Animals.serializer(), Animals(listOf(Animal.Dog("Spock"))), output)

output.toString(Charsets.UTF_8) shouldBe
"""
animals:
- type: "com.charleskorn.kaml.Animal.Dog"
name: "Spock"

""".trimIndent()
}
}

it("should support polymorphic writing with tag") {
with(
Yaml(
configuration = YamlConfiguration(
polymorphismStyle = PolymorphismStyle.Tag,
),
),
) {
val output = ByteArrayOutputStream()
encodeToStream(Animals.serializer(), Animals(listOf(Animal.Dog("Spock"))), output)

output.toString(Charsets.UTF_8) shouldBe
"""
animals:
- !<com.charleskorn.kaml.Animal.Dog>
name: "Spock"

""".trimIndent()
}
}

it("should support polymorphic writing no tag or property") {
with(
Yaml(
configuration = YamlConfiguration(
polymorphismStyle = PolymorphismStyle.None,
),
),
) {
val output = ByteArrayOutputStream()
encodeToStream(Animals.serializer(), Animals(listOf(Animal.Dog("Spock"))), output)

output.toString(Charsets.UTF_8) shouldBe
"""
animals:
- name: "Spock"

""".trimIndent()
}
}
}

describe("writing to a stream via generic extension function") {
Expand All @@ -115,3 178,15 @@ class JvmYamlWritingTest : DescribeSpec({
}
}
})

@Serializable
class Animals(val animals: List<Animal>)

@Serializable
sealed interface Animal {
@Serializable
data class Dog(val name: String) : Animal

@Serializable
data class Cat(val name: String) : Animal
}