Skip to content

Commit

Permalink
Make attribute names with default annotation values non-qualified by
Browse files Browse the repository at this point in the history
default.
  • Loading branch information
pdvrieze committed Jul 27, 2023
1 parent f2b3b3e commit fbfb866
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 42 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)<br />*
Expand Down
8 changes: 5 additions & 3 deletions serialization/api/android/serialization.api
Original file line number Diff line number Diff line change
Expand Up @@ -569,15 +569,17 @@ public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$Co
}

public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo {
public fun <init> (Ljava/lang/String;Ljavax/xml/namespace/QName;)V
public fun <init> (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;
}

Expand Down
8 changes: 5 additions & 3 deletions serialization/api/jvm/serialization.api
Original file line number Diff line number Diff line change
Expand Up @@ -569,15 +569,17 @@ public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$Co
}

public final class nl/adaptivity/xmlutil/serialization/XmlSerializationPolicy$DeclaredNameInfo {
public fun <init> (Ljava/lang/String;Ljavax/xml/namespace/QName;)V
public fun <init> (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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand All @@ -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)


Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -434,11 +435,11 @@ public class XML constructor(
?: serialDescriptor.annotations.firstOrNull<XmlSerialName>()
?.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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -222,7 +229,8 @@ public interface XmlSerializationPolicy {
* first element.
*/
@ExperimentalXmlUtilApi
public fun attributeListDelimiters(serializerParent: SafeParentInfo, tagParent: SafeParentInfo): Array<String> = arrayOf(" ", "\n", "\t", "\r")
public fun attributeListDelimiters(serializerParent: SafeParentInfo, tagParent: SafeParentInfo): Array<String> =
arrayOf(" ", "\n", "\t", "\r")

public enum class XmlEncodeDefault {
ALWAYS, ANNOTATED, NEVER
Expand All @@ -236,21 +244,27 @@ public interface XmlSerializationPolicy {
* correct child).
*/
@ExperimentalXmlUtilApi
public fun recoverNullNamespaceUse(inputKind: InputKind, descriptor: XmlDescriptor, name: QName?): List<XML.ParsedData<*>>? {
public fun recoverNullNamespaceUse(
inputKind: InputKind,
descriptor: XmlDescriptor,
name: QName?
): List<XML.ParsedData<*>>? {
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))
}
}
} else {
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))
}
}
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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<XmlChildrenName>()?.toQName()
return DeclaredNameInfo("value", childrenName)
val childAnnotation = serializerParent.elementUseAnnotations.firstOrNull<XmlChildrenName>()
val childrenName = childAnnotation?.toQName()
return DeclaredNameInfo("value", childrenName, childAnnotation?.namespace == UNSET_ANNOTATION_VALUE)
}

override fun mapEntryName(serializerParent: SafeParentInfo, isListEluded: Boolean): QName {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<XmlSerialName>()
?.toQName(serialName, tagParent.namespace)
val childUseNameInfo = DeclaredNameInfo(serialName, qName)
val annotation = typeDescriptor.serialDescriptor.getElementAnnotations(0).firstOrNull<XmlSerialName>()
val qName = annotation?.toQName(serialName, tagParent.namespace)
val childUseNameInfo =
DeclaredNameInfo(serialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)

when {
childUseNameInfo.annotatedName != null -> childUseNameInfo
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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<XmlSerialName>()?.toQName(serialName, parentNamespace)
return DeclaredNameInfo(serialName, qName)
val annotation = getElementAnnotations(index).firstOrNull<XmlSerialName>()
val qName = annotation?.toQName(serialName, parentNamespace)
return DeclaredNameInfo(serialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)
}

@ExperimentalSerializationApi
Expand All @@ -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<XmlSerialName>()?.toQName(realSerialName, parentNamespace)
return DeclaredNameInfo(realSerialName, qName)
val annotation = annotations.firstOrNull<XmlSerialName>()
val qName = annotation?.toQName(realSerialName, parentNamespace)
return DeclaredNameInfo(realSerialName, qName, annotation?.namespace == UNSET_ANNOTATION_VALUE)
}

public sealed class XmlListLikeDescriptor(
Expand Down Expand Up @@ -1111,7 +1122,7 @@ public class XmlListDescriptor internal constructor(

tagParent.elementUseAnnotations.firstOrNull<XmlValue>() != null &&
config.policy.isTransparentPolymorphic(
DetachedParent(serialDescriptor.getElementDescriptor(0), null, false),
DetachedParent(serialDescriptor.getElementDescriptor(0), false),
tagParent
)
-> OutputKind.Mixed
Expand All @@ -1132,10 +1143,10 @@ public class XmlListDescriptor internal constructor(
}

private val childDescriptor: XmlDescriptor by lazy {
val childrenName = tagParent.elementUseAnnotations.firstOrNull<XmlChildrenName>()?.toQName()
val childrenNameAnnotation = tagParent.elementUseAnnotations.firstOrNull<XmlChildrenName>()

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)

Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class MapTest : PlatformTestBase<MapTest.ListContainer>(
val xml = baseXmlFormat.copy {
policy = object : DefaultXmlSerializationPolicy(policy) {
override fun mapKeyName(serializerParent: SafeParentInfo): XmlSerializationPolicy.DeclaredNameInfo {
return XmlSerializationPolicy.DeclaredNameInfo("name", null)
return XmlSerializationPolicy.DeclaredNameInfo("name")
}
}
}
Expand Down

0 comments on commit fbfb866

Please sign in to comment.