diff --git a/Changelog.md b/Changelog.md
index 88f492292..8969b24a6 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,8 @@
Features:
Fixes:
+- When attributes have an `@XmlSerialName` annotation with a default namespace
+ value, then this will result in a non-qualified attribute.
# 0.86.1
*(July 5, 2023)
*
diff --git a/serialization/api/android/serialization.api b/serialization/api/android/serialization.api
index a0cfb4248..9620c24e7 100644
--- a/serialization/api/android/serialization.api
+++ b/serialization/api/android/serialization.api
@@ -569,15 +569,17 @@ public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$Co
}
public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo {
- public fun (Ljava/lang/String;Ljavax/xml/namespace/QName;)V
+ public fun (Ljava/lang/String;Ljavax/xml/namespace/QName;Z)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljavax/xml/namespace/QName;
- public final fun copy (Ljava/lang/String;Ljavax/xml/namespace/QName;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
- public static synthetic fun copy$default (Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;Ljava/lang/String;Ljavax/xml/namespace/QName;ILjava/lang/Object;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
+ public final fun component3 ()Z
+ public final fun copy (Ljava/lang/String;Ljavax/xml/namespace/QName;Z)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
+ public static synthetic fun copy$default (Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;Ljava/lang/String;Ljavax/xml/namespace/QName;ZILjava/lang/Object;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
public fun equals (Ljava/lang/Object;)Z
public final fun getAnnotatedName ()Ljavax/xml/namespace/QName;
public final fun getSerialName ()Ljava/lang/String;
public fun hashCode ()I
+ public final fun isDefaultNamespace ()Z
public fun toString ()Ljava/lang/String;
}
diff --git a/serialization/api/jvm/serialization.api b/serialization/api/jvm/serialization.api
index a0cfb4248..9620c24e7 100644
--- a/serialization/api/jvm/serialization.api
+++ b/serialization/api/jvm/serialization.api
@@ -569,15 +569,17 @@ public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$Co
}
public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo {
- public fun (Ljava/lang/String;Ljavax/xml/namespace/QName;)V
+ public fun (Ljava/lang/String;Ljavax/xml/namespace/QName;Z)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljavax/xml/namespace/QName;
- public final fun copy (Ljava/lang/String;Ljavax/xml/namespace/QName;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
- public static synthetic fun copy$default (Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;Ljava/lang/String;Ljavax/xml/namespace/QName;ILjava/lang/Object;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
+ public final fun component3 ()Z
+ public final fun copy (Ljava/lang/String;Ljavax/xml/namespace/QName;Z)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
+ public static synthetic fun copy$default (Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;Ljava/lang/String;Ljavax/xml/namespace/QName;ZILjava/lang/Object;)Lnl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo;
public fun equals (Ljava/lang/Object;)Z
public final fun getAnnotatedName ()Ljavax/xml/namespace/QName;
public final fun getSerialName ()Ljava/lang/String;
public fun hashCode ()I
+ public final fun isDefaultNamespace ()Z
public fun toString ()Ljava/lang/String;
}
diff --git a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XML.kt b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XML.kt
index 367ba62b4..cc952f18b 100644
--- a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XML.kt
+++ b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XML.kt
@@ -33,6 +33,7 @@ import nl.adaptivity.xmlutil.core.impl.multiplatform.Language
import nl.adaptivity.xmlutil.core.impl.multiplatform.StringWriter
import nl.adaptivity.xmlutil.core.impl.multiplatform.use
import nl.adaptivity.xmlutil.serialization.XML.Companion.encodeToWriter
+import nl.adaptivity.xmlutil.serialization.XmlSerializationPolicy.DeclaredNameInfo
import nl.adaptivity.xmlutil.serialization.impl.*
import nl.adaptivity.xmlutil.serialization.impl.ChildCollector
import nl.adaptivity.xmlutil.serialization.impl.NamespaceCollectingXmlWriter
@@ -162,7 +163,7 @@ public class XML constructor(
target.indentString = config.indentString
if (prefix != null) {
- val root = XmlRootDescriptor(config, serializersModule, serializer.descriptor, null)
+ val root = XmlRootDescriptor(config, serializersModule, serializer.descriptor)
val serialQName = root.getElementDescriptor(0).tagName.copy(prefix = prefix)
@@ -206,7 +207,7 @@ public class XML constructor(
}
}
- val root = XmlRootDescriptor(config, serializersModule, serializer.descriptor, rootName)
+ val root = XmlRootDescriptor(config, serializersModule, serializer.descriptor, rootName, false)
val xmlDescriptor = root.getElementDescriptor(0)
@@ -220,7 +221,7 @@ public class XML constructor(
})
val remappedEncoderBase = XmlEncoderBase(serializersModule, newConfig, target)
val newRootName = rootName?.remapPrefix(prefixMap)
- val newRoot = XmlRootDescriptor(newConfig, serializersModule, serializer.descriptor, newRootName)
+ val newRoot = XmlRootDescriptor(newConfig, serializersModule, serializer.descriptor, newRootName, false)
val newDescriptor = newRoot.getElementDescriptor(0)
@@ -398,7 +399,7 @@ public class XML constructor(
reader.skipPreamble()
val xmlDecoderBase = XmlDecoderBase(serializersModule, config, reader)
- val rootDescriptor = XmlRootDescriptor(config, serializersModule, deserializer.descriptor, serialName)
+ val rootDescriptor = XmlRootDescriptor(config, serializersModule, deserializer.descriptor, serialName, false)
val elementDescriptor = rootDescriptor.getElementDescriptor(0)
val polyInfo = (elementDescriptor as? XmlPolymorphicDescriptor)?.run {
@@ -434,11 +435,11 @@ public class XML constructor(
?: serialDescriptor.annotations.firstOrNull()
?.toQName(serialDescriptor.serialName, null)
?: config.policy.serialTypeNameToQName(
- XmlSerializationPolicy.DeclaredNameInfo(serialDescriptor.serialName, null),
+ DeclaredNameInfo(serialDescriptor.serialName),
XmlEvent.NamespaceImpl(XMLConstants.DEFAULT_NS_PREFIX, XMLConstants.NULL_NS_URI)
)
- return XmlRootDescriptor(config, serializersModule, serialDescriptor, serialName)
+ return XmlRootDescriptor(config, serializersModule, serialDescriptor, serialName, false)
}
@Deprecated("Use config directly", ReplaceWith("config.repairNamespaces"), DeprecationLevel.HIDDEN)
diff --git a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy.kt b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy.kt
index d99caf61c..81ca11f83 100644
--- a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy.kt
+++ b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy.kt
@@ -97,8 +97,15 @@ public interface XmlSerializationPolicy {
public data class DeclaredNameInfo(
val serialName: String,
- val annotatedName: QName?
- )
+ val annotatedName: QName?,
+ val isDefaultNamespace: Boolean/* = false*/
+ ) {
+ internal constructor(serialName: String) : this(serialName, null, false)
+
+ init {
+ check(!(isDefaultNamespace && annotatedName == null)) { "Default namespace requires there to be an annotated name" }
+ }
+ }
public data class ActualNameInfo(
val serialName: String,
@@ -190,13 +197,13 @@ public interface XmlSerializationPolicy {
/** Determine the name of map keys for a given map type */
public fun mapKeyName(serializerParent: SafeParentInfo): DeclaredNameInfo =
- DeclaredNameInfo("key", null) // minimal default for implementations.
+ DeclaredNameInfo("key") // minimal default for implementations.
/**
* Determine the name of the values for a given map type
*/
public fun mapValueName(serializerParent: SafeParentInfo, isListEluded: Boolean): DeclaredNameInfo =
- DeclaredNameInfo("value", null) // minimal default for implementations.
+ DeclaredNameInfo("value") // minimal default for implementations.
/**
* Determine the name to use for the map element (only used when a map entry is wrapped)
@@ -222,7 +229,8 @@ public interface XmlSerializationPolicy {
* first element.
*/
@ExperimentalXmlUtilApi
- public fun attributeListDelimiters(serializerParent: SafeParentInfo, tagParent: SafeParentInfo): Array = arrayOf(" ", "\n", "\t", "\r")
+ public fun attributeListDelimiters(serializerParent: SafeParentInfo, tagParent: SafeParentInfo): Array =
+ arrayOf(" ", "\n", "\t", "\r")
public enum class XmlEncodeDefault {
ALWAYS, ANNOTATED, NEVER
@@ -236,13 +244,18 @@ public interface XmlSerializationPolicy {
* correct child).
*/
@ExperimentalXmlUtilApi
- public fun recoverNullNamespaceUse(inputKind: InputKind, descriptor: XmlDescriptor, name: QName?): List>? {
+ public fun recoverNullNamespaceUse(
+ inputKind: InputKind,
+ descriptor: XmlDescriptor,
+ name: QName?
+ ): List>? {
if (name != null) {
if (name.namespaceURI == "") {
for (idx in 0 until descriptor.elementsCount) {
val candidate = descriptor.getElementDescriptor(idx)
if (inputKind.mapsTo(candidate.effectiveOutputKind) &&
- candidate.tagName.localPart == name.getLocalPart()) {
+ candidate.tagName.localPart == name.getLocalPart()
+ ) {
return listOf(XML.ParsedData(idx, Unit, true))
}
}
@@ -250,7 +263,8 @@ public interface XmlSerializationPolicy {
for (idx in 0 until descriptor.elementsCount) {
val candidate = descriptor.getElementDescriptor(idx)
if (inputKind.mapsTo(candidate.effectiveOutputKind) &&
- candidate.tagName.isEquivalent(QName(name.localPart))) {
+ candidate.tagName.isEquivalent(QName(name.localPart))
+ ) {
return listOf(XML.ParsedData(idx, Unit, true))
}
}
@@ -495,9 +509,14 @@ public open class DefaultXmlSerializationPolicy
val parentSerialKind = tagParent.descriptor?.serialKind
return when {
+ outputKind == OutputKind.Attribute -> when {
+ !useName.isDefaultNamespace -> useName.annotatedName!! // invariant enforced by type
+ useName.annotatedName != null -> QName(useName.annotatedName.getLocalPart()) // use unqualified attribute name
+ else -> QName(useName.serialName)
+ } // Use non-prefix attributes by default
+
useName.annotatedName != null -> useName.annotatedName
- outputKind == OutputKind.Attribute -> QName(useName.serialName) // Use non-prefix attributes by default
serialKind is PrimitiveKind ||
serialKind == StructureKind.MAP ||
@@ -652,12 +671,13 @@ public open class DefaultXmlSerializationPolicy
}
override fun mapKeyName(serializerParent: SafeParentInfo): DeclaredNameInfo {
- return DeclaredNameInfo("key", null)
+ return DeclaredNameInfo("key")
}
override fun mapValueName(serializerParent: SafeParentInfo, isListEluded: Boolean): DeclaredNameInfo {
- val childrenName = serializerParent.elementUseAnnotations.firstOrNull()?.toQName()
- return DeclaredNameInfo("value", childrenName)
+ val childAnnotation = serializerParent.elementUseAnnotations.firstOrNull()
+ val childrenName = childAnnotation?.toQName()
+ return DeclaredNameInfo("value", childrenName, childAnnotation?.namespace == UNSET_ANNOTATION_VALUE)
}
override fun mapEntryName(serializerParent: SafeParentInfo, isListEluded: Boolean): QName {
diff --git a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/structure/XmlDescriptor.kt b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/structure/XmlDescriptor.kt
index cedd90a34..4e4671c60 100644
--- a/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/structure/XmlDescriptor.kt
+++ b/serialization/src/commonMain/kotlin/nl/adaptivity/xmlutil/serialization/structure/XmlDescriptor.kt
@@ -310,7 +310,15 @@ public class XmlRootDescriptor internal constructor(
serializersModule: SerializersModule,
descriptor: SerialDescriptor,
tagName: QName?,
-) : XmlDescriptor(config.policy, DetachedParent(descriptor, tagName, true, outputKind = null)) {
+ isDefaultNamespace: Boolean,
+) : XmlDescriptor(config.policy, DetachedParent(descriptor, tagName, true, outputKind = null, isDefaultNamespace = isDefaultNamespace)) {
+
+ internal constructor(
+// TODO get rid of coded, put policy in its place
+ config: XmlConfig,
+ serializersModule: SerializersModule,
+ descriptor: SerialDescriptor,
+ ) : this (config, serializersModule, descriptor, null, false)
private val element: XmlDescriptor by lazy {
from(config, serializersModule, tagParent, canBeAttribute = false)
@@ -536,9 +544,10 @@ public class XmlInlineDescriptor internal constructor(
// This is needed as this descriptor is not complete yet and would use this element's
// unset name for the namespace.
val serialName = typeDescriptor.serialDescriptor.getElementName(0)
- val qName = typeDescriptor.serialDescriptor.getElementAnnotations(0).firstOrNull()
- ?.toQName(serialName, tagParent.namespace)
- val childUseNameInfo = DeclaredNameInfo(serialName, qName)
+ val annotation = typeDescriptor.serialDescriptor.getElementAnnotations(0).firstOrNull()
+ val qName = annotation?.toQName(serialName, tagParent.namespace)
+ val childUseNameInfo =
+ DeclaredNameInfo(serialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)
when {
childUseNameInfo.annotatedName != null -> childUseNameInfo
@@ -864,7 +873,7 @@ public class XmlPolymorphicDescriptor internal constructor(
for (polyChild in xmlPolyChildren.value) {
val childInfo = polyTagName(baseName, polyChild, baseClass, serializersModule)
- val childSerializerParent = DetachedParent(childInfo.descriptor, childInfo.tagName, false)
+ val childSerializerParent = DetachedParent(childInfo.descriptor, childInfo.tagName, false, isDefaultNamespace = false)
map[childInfo.describedName] =
from(config, serializersModule, childSerializerParent, tagParent, canBeAttribute = false)
@@ -876,7 +885,7 @@ public class XmlPolymorphicDescriptor internal constructor(
val d = serialDescriptor.getElementDescriptor(1)
for (i in 0 until d.elementsCount) {
val childDesc = d.getElementDescriptor(i)
- val childSerializerParent = DetachedParent(childDesc, qName, false)
+ val childSerializerParent = DetachedParent(childDesc, qName, false, isDefaultNamespace = false)
map[childDesc.serialName] =
from(config, serializersModule, childSerializerParent, tagParent, canBeAttribute = false)
@@ -890,7 +899,7 @@ public class XmlPolymorphicDescriptor internal constructor(
for (childDesc in childDescriptors) {
- val childSerializerParent = DetachedParent(childDesc, qName, false, outputKind)
+ val childSerializerParent = DetachedParent(childDesc, qName, false, outputKind, isDefaultNamespace = false)
map[childDesc.serialName] =
from(config, serializersModule, childSerializerParent, tagParent, canBeAttribute = false)
@@ -980,8 +989,9 @@ public class XmlPolymorphicDescriptor internal constructor(
@ExperimentalSerializationApi
internal fun SerialDescriptor.getElementNameInfo(index: Int, parentNamespace: Namespace?): DeclaredNameInfo {
val serialName = getElementName(index)
- val qName = getElementAnnotations(index).firstOrNull()?.toQName(serialName, parentNamespace)
- return DeclaredNameInfo(serialName, qName)
+ val annotation = getElementAnnotations(index).firstOrNull()
+ val qName = annotation?.toQName(serialName, parentNamespace)
+ return DeclaredNameInfo(serialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)
}
@ExperimentalSerializationApi
@@ -990,8 +1000,9 @@ internal fun SerialDescriptor.getNameInfo(parentNamespace: Namespace?): Declared
isNullable && serialName.endsWith('?') -> serialName.dropLast(1)
else -> capturedKClass?.serialName ?: serialName
}
- val qName = annotations.firstOrNull()?.toQName(realSerialName, parentNamespace)
- return DeclaredNameInfo(realSerialName, qName)
+ val annotation = annotations.firstOrNull()
+ val qName = annotation?.toQName(realSerialName, parentNamespace)
+ return DeclaredNameInfo(realSerialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)
}
public sealed class XmlListLikeDescriptor(
@@ -1111,7 +1122,7 @@ public class XmlListDescriptor internal constructor(
tagParent.elementUseAnnotations.firstOrNull() != null &&
config.policy.isTransparentPolymorphic(
- DetachedParent(serialDescriptor.getElementDescriptor(0), null, false),
+ DetachedParent(serialDescriptor.getElementDescriptor(0), false),
tagParent
)
-> OutputKind.Mixed
@@ -1132,10 +1143,10 @@ public class XmlListDescriptor internal constructor(
}
private val childDescriptor: XmlDescriptor by lazy {
- val childrenName = tagParent.elementUseAnnotations.firstOrNull()?.toQName()
+ val childrenNameAnnotation = tagParent.elementUseAnnotations.firstOrNull()
val useNameInfo = when {
- childrenName != null -> DeclaredNameInfo(childrenName.localPart, childrenName)
+ childrenNameAnnotation != null -> DeclaredNameInfo(childrenNameAnnotation.value, childrenNameAnnotation.toQName(), childrenNameAnnotation?.namespace == UNSET_ANNOTATION_VALUE)
!isListEluded -> null // if we have a list, don't repeat the outer name (at least allow the policy to decide)
@@ -1286,10 +1297,23 @@ private class DetachedParent(
serialDescriptor: SerialDescriptor,
useName: QName?,
isDocumentRoot: Boolean,
- outputKind: OutputKind? = null
+ outputKind: OutputKind? = null,
+ isDefaultNamespace: Boolean,
+ ) : this(
+ serialDescriptor,
+ DeclaredNameInfo(serialDescriptor.run { capturedKClass?.serialName ?: serialName }, useName, isDefaultNamespace),
+ isDocumentRoot,
+ outputKind
+ )
+
+ @OptIn(ExperimentalSerializationApi::class)
+ constructor(
+ serialDescriptor: SerialDescriptor,
+ isDocumentRoot: Boolean,
+ outputKind: OutputKind? = null,
) : this(
serialDescriptor,
- DeclaredNameInfo(serialDescriptor.run { capturedKClass?.serialName ?: serialName }, useName),
+ DeclaredNameInfo(serialDescriptor.run { capturedKClass?.serialName ?: serialName }, null, false),
isDocumentRoot,
outputKind
)
@@ -1409,7 +1433,7 @@ public class ParentInfo(
@OptIn(ExperimentalSerializationApi::class)
override val elementUseNameInfo: DeclaredNameInfo = useNameInfo ?: when (index) {
- -1 -> DeclaredNameInfo(descriptor.serialDescriptor.serialName, null)
+ -1 -> DeclaredNameInfo(descriptor.serialDescriptor.serialName)
else -> descriptor.serialDescriptor.getElementNameInfo(index, descriptor.tagName.toNamespace())
}
diff --git a/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/MapTest.kt b/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/MapTest.kt
index a0c655330..b007af0e3 100644
--- a/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/MapTest.kt
+++ b/serialization/src/commonTest/kotlin/nl/adaptivity/xml/serialization/MapTest.kt
@@ -136,7 +136,7 @@ class MapTest : PlatformTestBase(
val xml = baseXmlFormat.copy {
policy = object : DefaultXmlSerializationPolicy(policy) {
override fun mapKeyName(serializerParent: SafeParentInfo): XmlSerializationPolicy.DeclaredNameInfo {
- return XmlSerializationPolicy.DeclaredNameInfo("name", null)
+ return XmlSerializationPolicy.DeclaredNameInfo("name")
}
}
}