Skip to content

Commit

Permalink
Fix parsing of default values for attributes that use serializers.
Browse files Browse the repository at this point in the history
  • Loading branch information
pdvrieze committed Jan 2, 2024
1 parent fbd9a7d commit 312ce62
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 12 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Features:
interfaces allows the format to treat the data specially.

Fixes:
- Fix parsing of `XmlDefault` attributes if the (effective) type is an
attribute and it is parsed using as serializable value (rather than)
directly as primitive.
- Using an attribute map wouldn't work when the key was a string rather than
a qname. Fixes #190.
- Properly require `@XmlOtherAttributes` for maps of "remaining"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.StructureKind
Expand Down Expand Up @@ -174,7 +175,6 @@ internal open class XmlDecoderBase internal constructor(

@OptIn(ExperimentalXmlUtilApi::class)
override fun decodeStringImpl(defaultOverEmpty: Boolean): String {
val defaultString = (xmlDescriptor as? XmlValueDescriptor)?.default
val descOutputKind = xmlDescriptor.outputKind

val stringValue = if (attrIndex >= 0) {
Expand All @@ -200,7 +200,12 @@ internal open class XmlDecoderBase internal constructor(
}
}
return when {
defaultOverEmpty && stringValue.isEmpty() && defaultString != null -> defaultString
defaultOverEmpty && stringValue.isEmpty() ->
when (val defaultString = (xmlDescriptor as? XmlValueDescriptor)?.default) {
null -> stringValue
else -> defaultString
}

else -> stringValue
}
}
Expand Down Expand Up @@ -267,7 +272,7 @@ internal open class XmlDecoderBase internal constructor(

}

private inner class StringDecoder(xmlDescriptor: XmlDescriptor, private val stringValue: String) :
internal inner class StringDecoder(xmlDescriptor: XmlDescriptor, private val stringValue: String) :
Decoder, XML.XmlInput, DecodeCommons(xmlDescriptor) {

override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
Expand Down Expand Up @@ -490,7 +495,18 @@ internal open class XmlDecoderBase internal constructor(
private inner class NullDecoder(xmlDescriptor: XmlDescriptor) :
XmlDecoder(xmlDescriptor), CompositeDecoder {

override fun decodeNotNullMark() = false
override fun decodeNotNullMark() = (xmlDescriptor as? XmlValueDescriptor)?.default != null

override fun decodeStringImpl(defaultOverEmpty: Boolean): String {
val default = (xmlDescriptor as? XmlValueDescriptor)?.defaultValue(this@XmlDecoderBase, String.serializer())
return default as String
}

override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
val default = (xmlDescriptor as? XmlValueDescriptor)?.defaultValue(this@XmlDecoderBase, deserializer)
@Suppress("UNCHECKED_CAST")
return default as T
}

override fun <T> decodeSerializableElement(
descriptor: SerialDescriptor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2018.
* Copyright (c) 2023.
*
* This file is part of XmlUtil.
* 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
Expand Down Expand Up @@ -83,7 +83,7 @@ internal abstract class XmlCodecBase internal constructor(
}

@Suppress("RedundantInnerClassModifier") // The actual children must be inner
abstract inner class XmlCodec<out D : SafeXmlDescriptor>(
abstract class XmlCodec<out D : SafeXmlDescriptor>(
protected val xmlDescriptor: D
) {
val serialName: QName get() = xmlDescriptor.tagName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.builtins.nullable
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
import nl.adaptivity.xmlutil.*
import nl.adaptivity.xmlutil.core.impl.multiplatform.MpJvmDefaultWithCompatibility
Expand Down Expand Up @@ -413,16 +412,39 @@ public sealed class XmlValueDescriptor(
private var defaultValue: Any? = UNSET

@Deprecated("This is not safe anymore. This should have been internal.")
@XmlUtilDeprecatedInternal
public fun <T> defaultValue(deserializer: DeserializationStrategy<T>): T {
return defaultValue(EmptySerializersModule(), XmlConfig(), deserializer)
val codec = XmlDecoderBase(getPlatformDefaultModule(), XmlConfig(), CompactFragment(default ?: "").getXmlReader())

return defaultValue(codec, deserializer)
}

internal fun <T> defaultValue(
xmlCodecBase: XmlCodecBase,
deserializer: DeserializationStrategy<T>
): T {
defaultValue.let { d ->
@Suppress("UNCHECKED_CAST")
if (d != UNSET) return d as T
}

@Suppress("UNCHECKED_CAST")
if (default == null) return null as T

return defaultValue(xmlCodecBase.serializersModule, xmlCodecBase.config, deserializer)
return when {
effectiveOutputKind.let { it != OutputKind.Text && it != OutputKind.Text } ->
defaultValue(xmlCodecBase.serializersModule, xmlCodecBase.config, deserializer)

xmlCodecBase is XmlDecoderBase ->
deserializer.deserialize(xmlCodecBase.StringDecoder(this, default))

else -> xmlCodecBase.run {
val dec = XmlDecoderBase(serializersModule, config, CompactFragment("").getXmlReader())
.StringDecoder(this@XmlValueDescriptor, default)

deserializer.deserialize(dec)
}
}
}

private fun <T> defaultValue(
Expand All @@ -434,8 +456,16 @@ public sealed class XmlValueDescriptor(
@Suppress("UNCHECKED_CAST")
if (d != UNSET) return d as T
}
val d = when (default) {
null -> null
val d = when {
default == null -> null

effectiveOutputKind.let { it == OutputKind.Attribute || it == OutputKind.Text } -> {
val xmlDecoderBase: XmlDecoderBase = XmlDecoderBase(serializersModule, config, CompactFragment(default).getXmlReader())
val dec = xmlDecoderBase
.StringDecoder(this, default)
deserializer.deserialize(dec)
}

else -> {
val defaultDecoder =
XmlDecoderBase(
Expand Down

0 comments on commit 312ce62

Please sign in to comment.