Skip to content

Commit

Permalink
Add support for contextual serializers (also in the XmlDescriptor). This
Browse files Browse the repository at this point in the history
is not supported in decoding yet.
  • Loading branch information
pdvrieze committed May 24, 2024
1 parent 5dda05e commit 640097f
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,16 @@ internal open class XmlEncoderBase internal constructor(
serializer: SerializationStrategy<T>,
value: T
) {
xmlDescriptor.effectiveSerializationStrategy(serializer).serializeSafe(this, value)
when {
xmlDescriptor is XmlContextualDescriptor -> {
val actualDescriptor = xmlDescriptor.resolve(serializer)
val delegateEncoder = XmlEncoder(actualDescriptor, elementIndex, discriminatorName)
actualDescriptor.effectiveSerializationStrategy(serializer).serializeSafe(delegateEncoder, value)
}

else ->
xmlDescriptor.effectiveSerializationStrategy(serializer).serializeSafe(this, value)
}
}

@ExperimentalSerializationApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ public sealed class XmlDescriptor(
final override val tagParent: SafeParentInfo = serializerParent,
) : SafeXmlDescriptor {

/**
* Does this value represent an xml ID attribute (requiring global uniqueness)
*/
public abstract val isIdAttr: Boolean

public val effectiveOutputKind: OutputKind
Expand Down Expand Up @@ -163,6 +166,7 @@ public sealed class XmlDescriptor(
@OptIn(ExperimentalSerializationApi::class)
internal fun <A : Appendable> toString(builder: A, indent: Int, seen: MutableSet<String>): A {
when (this) {
is XmlContextualDescriptor,
is XmlListDescriptor,
is XmlPrimitiveDescriptor -> appendTo(builder, indent, seen)

Expand Down Expand Up @@ -293,6 +297,9 @@ public sealed class XmlDescriptor(
effectiveTagParent
)

SerialKind.CONTEXTUAL ->
return XmlContextualDescriptor(config, serializersModule, effectiveSerializerParent, effectiveTagParent, canBeAttribute)

else -> {} // fall through to other handler.
}

Expand Down Expand Up @@ -728,6 +735,39 @@ public class XmlAttributeMapDescriptor internal constructor(

}

public class XmlContextualDescriptor @ExperimentalXmlUtilApi
internal constructor(
private val config: XmlConfig,
private val serializersModule: SerializersModule,
serializerParent: SafeParentInfo,
tagParent: SafeParentInfo,
private val canBeAttribute: Boolean
) : XmlDescriptor(config.policy, serializerParent, tagParent) {
@ExperimentalSerializationApi
override val doInline: Boolean get() = false

override val isIdAttr: Boolean get() = false


override fun appendTo(builder: Appendable, indent: Int, seen: MutableSet<String>) {
builder
.append("CONTEXTUAL(")
.append(tagParent.elementUseNameInfo.run { annotatedName?.toString() ?: serialName })
.append(")")
}

internal fun <T> resolve(serializer: SerializationStrategy<T>): XmlDescriptor {
val overriddenParentInfo = DetachedParent(serializer.descriptor, useNameInfo, false)

return from(config, serializersModule, overriddenParentInfo, tagParent, canBeAttribute)
}

@ExperimentalXmlUtilApi
override val preserveSpace: Boolean get() = false

override val outputKind: OutputKind get() = OutputKind.Inline
}

public class XmlCompositeDescriptor @ExperimentalXmlUtilApi
internal constructor(
config: XmlConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024.
*
* This file is part of xmlutil.
*
* This file is licenced to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You should have received a copy of the license with the source distribution.
* Alternatively, you may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package nl.adaptivity.xml.serialization.regressions

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import nl.adaptivity.xmlutil.QName
import nl.adaptivity.xmlutil.core.impl.multiplatform.name
import nl.adaptivity.xmlutil.serialization.XML
import nl.adaptivity.xmlutil.serialization.structure.XmlContextualDescriptor
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

class ContextualSerialization208 {
val xml = XML(
SerializersModule {
contextual(AnyIntSerializer)
}
) { recommended_0_87_0() }

@Test
fun testSerialization() {
val data = TestContainer(101, 102)

val actual = xml.encodeToString(data)
val expected = "<TestContainer any1=\"101\" any2=\"102\"/>"
assertEquals(expected, actual)
}

@Test
fun textXmlDescriptor() {
val data = TestContainer(101, 102)
val descriptor = xml.xmlDescriptor(TestContainer.serializer()).getElementDescriptor(0)

assertEquals(QName("TestContainer"), descriptor.tagName)
assertEquals(2, descriptor.elementsCount)
val child1Descriptor = assertIs<XmlContextualDescriptor>(descriptor.getElementDescriptor(0))
val child2Descriptor = assertIs<XmlContextualDescriptor>(descriptor.getElementDescriptor(1))
}

object AnyIntSerializer: KSerializer<Any> {
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("AnyInt", PrimitiveKind.INT)

override fun serialize(encoder: Encoder, value: Any) {
encoder.encodeInt(value as Int)
}

override fun deserialize(decoder: Decoder): Any {
return decoder.decodeInt()
}
}

@Serializable
class TestContainer(@Contextual val any1: Any, @Contextual val any2: Any)
}

0 comments on commit 640097f

Please sign in to comment.