diff --git a/Changelog.md b/Changelog.md index e3167de7d..d5ff7b06e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,8 @@ Changes: This aids #225. Fixes: +- More reluctant caching of extended type descriptors such that type + parameters work correctly with caching. - Fix NodeSerializer in the serialization module to properly forward to the actual implementation. - Don't make the companion of `XmlDeclMode` internal (#219). This is a diff --git a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/DefaultFormatCache.kt b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/DefaultFormatCache.kt index ec3d5e729..2169c6617 100644 --- a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/DefaultFormatCache.kt +++ b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/DefaultFormatCache.kt @@ -27,7 +27,6 @@ import kotlinx.serialization.descriptors.SerialKind import kotlinx.serialization.descriptors.StructureKind import nl.adaptivity.xmlutil.Namespace import nl.adaptivity.xmlutil.QName -import nl.adaptivity.xmlutil.localPart import nl.adaptivity.xmlutil.namespaceURI import nl.adaptivity.xmlutil.serialization.XML.XmlCodecConfig import nl.adaptivity.xmlutil.serialization.structure.SafeParentInfo @@ -48,7 +47,7 @@ internal class DefaultFormatCache : FormatCache() { @OptIn(ExperimentalSerializationApi::class) override fun lookupType(namespace: Namespace?, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor { - return lookupType(TypeKey(namespace?.namespaceURI, serialDesc.serialName), serialDesc.kind, defaultValue) + return lookupType(TypeKey(namespace?.namespaceURI, serialDesc.serialName, serialDesc.hashCode()), serialDesc.kind, defaultValue) } /** @@ -57,12 +56,7 @@ internal class DefaultFormatCache : FormatCache() { */ @OptIn(ExperimentalSerializationApi::class) override fun lookupType(parentName: QName, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor { - return lookupType(TypeKey(parentName.namespaceURI, serialDesc.serialName), serialDesc.kind, defaultValue) - } - - @OptIn(ExperimentalSerializationApi::class) - override fun lookupType(name: QName, kind: SerialKind, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor { - return lookupType(TypeKey(name.namespaceURI, name.localPart), kind, defaultValue) + return lookupType(TypeKey(parentName.namespaceURI, serialDesc.serialName, serialDesc.hashCode()), serialDesc.kind, defaultValue) } @OptIn(ExperimentalSerializationApi::class) @@ -91,8 +85,6 @@ internal class DefaultFormatCache : FormatCache() { // This has to be getOrPut rather than `computeIfAbsent` as computeIfAbsent prevents other // changes to different types. GetOrPut does not have that property (but is technically slower) return elemDescCache.getOrPut(key) { -// val parentName = serializerParent.descriptor?.typeDescriptor?.run { typeQname ?: serialName } -// println("Calculating new descriptor for $parentName/${serializerParent.elementSerialDescriptor.serialName}") defaultValue() }.also { pendingDescs.remove(key) @@ -106,9 +98,6 @@ internal class DefaultFormatCache : FormatCache() { preserveSpace: Boolean ): XmlCompositeDescriptor { return XmlCompositeDescriptor(codecConfig, serializerParent, tagParent, preserveSpace) -// return lookupDescriptor(null, serializerParent, tagParent, false) { -// XmlCompositeDescriptor(config, serializersModule, serializerParent, tagParent, preserveSpace) -// } as XmlCompositeDescriptor } internal data class DescKey( @@ -118,11 +107,11 @@ internal class DefaultFormatCache : FormatCache() { val canBeAttribute: Boolean ) - private data class TypeKey(val namespace: String, val serialName: String) + private data class TypeKey(val namespace: String, val serialName: String, val descriptorHash: Int) companion object { @JvmStatic - private fun TypeKey(namespace: String?, serialName: String) = - DefaultFormatCache.TypeKey(namespace ?: "", serialName) + private fun TypeKey(namespace: String?, serialName: String, descriptorHash: Int) = + DefaultFormatCache.TypeKey(namespace ?: "", serialName, descriptorHash) } } diff --git a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/FormatCache.kt b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/FormatCache.kt index 9b7ea8339..17d0bfa78 100644 --- a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/FormatCache.kt +++ b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/FormatCache.kt @@ -20,10 +20,8 @@ package nl.adaptivity.xmlutil.serialization -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.SerialKind import nl.adaptivity.xmlutil.Namespace import nl.adaptivity.xmlutil.QName import nl.adaptivity.xmlutil.serialization.structure.SafeParentInfo @@ -40,9 +38,6 @@ public abstract class FormatCache internal constructor(){ */ internal abstract fun lookupType(parentName: QName, serialDesc: SerialDescriptor, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor - @OptIn(ExperimentalSerializationApi::class) - internal abstract fun lookupType(name: QName, kind: SerialKind, defaultValue: () -> XmlTypeDescriptor): XmlTypeDescriptor - internal abstract fun lookupDescriptor( overridenSerializer: KSerializer<*>?, serializerParent: SafeParentInfo, @@ -71,13 +66,6 @@ public abstract class FormatCache internal constructor(){ defaultValue: () -> XmlTypeDescriptor ): XmlTypeDescriptor = defaultValue() - @OptIn(ExperimentalSerializationApi::class) - override fun lookupType( - name: QName, - kind: SerialKind, - defaultValue: () -> XmlTypeDescriptor - ): XmlTypeDescriptor = defaultValue() - override fun lookupDescriptor( overridenSerializer: KSerializer<*>?, serializerParent: SafeParentInfo, diff --git a/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/regressions/TestCachedDescriptorCache.kt b/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/regressions/TestCachedDescriptorCache.kt new file mode 100644 index 000000000..f31132117 --- /dev/null +++ b/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/regressions/TestCachedDescriptorCache.kt @@ -0,0 +1,51 @@ +/* + * 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 io.github.pdvrieze.xmlutil.testutil.assertXmlEquals +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import nl.adaptivity.xmlutil.serialization.XML +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestCachedDescriptorCache { + + @Test + fun testParameterisedSerializerCache() { + val format = XML {} + + val serialized1 = format.encodeToString(Outer(Inner1(1, 2))) + assertXmlEquals("", serialized1) + + val serialized2 = format.encodeToString(Outer(Inner2("a", "b", "c"))) + assertXmlEquals("", serialized2) + } + + @Serializable + data class Outer(val data: T) + + @Serializable + data class Inner1(val data1: Int, val data2: Int) + + @Serializable + data class Inner2(val data3: String, val data4: String, val data5: String) +}