@@ -43,22 +43,10 @@ object CompatContainerSerializer: CommonContainerSerializer() {
4343
4444[ ContainerSerializer] ( src/main/kotlin/net/devrieze/serialization/examples/dynamictagnames/ContainerSerializer.kt )
4545``` kotlin
46- /* *
47- * This version of the serializer uses the new delegateFormat method on [XML.XmlInput] and [XML.XmlOutput]
48- * to inherit configuration and serializerModules.
49- */
50- object ContainerSerializer: CommonContainerSerializer() {
51- override fun delegateFormat (decoder : Decoder ) = (decoder as XML .XmlInput ).delegateFormat()
52- override fun delegateFormat (encoder : Encoder ) = (encoder as XML .XmlOutput ).delegateFormat()
53- }
54- ```
55-
56- [ CommonContainerSerializer] ( src/main/kotlin/net/devrieze/serialization/examples/dynamictagnames/CommonContainerSerializer.kt )
57- ``` kotlin
5846/* *
5947 * A common base class that contains the actual code needed to serialize/deserialize the container.
6048 */
61- abstract class CommonContainerSerializer : KSerializer <Container > {
49+ class ContainerSerializer : XmlSerializer <Container > {
6250 /* * We need to have the serializer for the elements */
6351 private val elementSerializer = serializer<TestElement >()
6452
@@ -69,103 +57,112 @@ abstract class CommonContainerSerializer: KSerializer<Container> {
6957
7058 override fun deserialize (decoder : Decoder ): Container {
7159 // XmlInput is designed as an interface to test for to allow custom serializers
72- if (decoder is XML .XmlInput ) { // We treat XML different, using a separate method for clarity
73- return deserializeDynamic(decoder, decoder.input)
74- } else { // Simple default decoder implementation that delegates parsing the data to the ListSerializer
75- val data = decoder.decodeStructure(descriptor) {
76- decodeSerializableElement(descriptor, 0 , ListSerializer (elementSerializer))
77- }
78- return Container (data)
60+ val data = decoder.decodeStructure(descriptor) {
61+ decodeSerializableElement(descriptor, 0 , ListSerializer (elementSerializer))
7962 }
63+ // Simple default decoder implementation that delegates parsing the data to the ListSerializer
64+ return Container (data)
8065 }
8166
82- /* *
83- * This function is the meat to deserializing the container with dynamic tag names. Note that
84- * because we use xml there is no point in going through the (anonymous) list dance. Doing that
85- * would be an additional complication.
86- */
87- fun deserializeDynamic (decoder : Decoder , reader : XmlReader ): Container {
88- val xml = delegateFormat(decoder) // get the format for deserializing
67+ override fun serialize (encoder : Encoder , value : Container ) {
68+ encoder.encodeStructure(descriptor) {
69+ encodeSerializableElement(descriptor, 0 , ListSerializer (elementSerializer), value.data)
70+ }
71+ }
72+
73+ override fun deserializeXML (
74+ decoder : Decoder ,
75+ input : XmlReader ,
76+ previousValue : Container ? ,
77+ isValueChild : Boolean
78+ ): Container {
79+ // This delegate format allows for reusing the settings from the outer format.
80+ val xml = (decoder as XML .XmlInput ).delegateFormat()
8981
9082 // We need the descriptor for the element. xmlDescriptor returns a rootDescriptor, so the actual descriptor is
9183 // its (only) child.
9284 val elementXmlDescriptor = xml.xmlDescriptor(elementSerializer).getElementDescriptor(0 )
93-
9485 // A list to collect the data
9586 val dataList = mutableListOf<TestElement >()
96-
9787 decoder.decodeStructure(descriptor) {
9888 // Finding the children is actually not left to the serialization framework, but
9989 // done by "hand"
100- while (reader .next() != EventType .END_ELEMENT ) {
101- when (reader .eventType) {
90+ while (input .next() != EventType .END_ELEMENT ) {
91+ when (input .eventType) {
10292 EventType .COMMENT ,
10393 EventType .IGNORABLE_WHITESPACE -> {
10494 // Comments and whitespace are just ignored
10595 }
106- EventType .ENTITY_REF ,
107- EventType .TEXT -> if (reader.text.isNotBlank()) {
108- // Some parsers can return whitespace as text instead of ignorable whitespace
10996
110- // Use the handler from the configuration to throw the exception.
111- xml.config.unknownChildHandler(reader, InputKind .Text , null , emptyList())
97+ EventType .ENTITY_REF ,
98+ EventType .TEXT -> {
99+ if (input.text.isNotBlank()) {
100+ // Some parsers can return whitespace as text instead of ignorable whitespace
101+
102+ // Use the handler from the configuration to throw the exception.
103+ @OptIn(ExperimentalXmlUtilApi ::class )
104+ xml.config.policy.handleUnknownContentRecovering(
105+ input,
106+ InputKind .Text ,
107+ elementXmlDescriptor,
108+ null ,
109+ emptyList()
110+ )
111+ }
112112 }
113113 // It's best to still check the name before parsing
114- EventType .START_ELEMENT -> if (reader.namespaceURI.isEmpty() && reader.localName.startsWith(" Test_" )) {
115- // When reading the child tag we use the DynamicTagReader to present normalized XML to the
116- // deserializer for elements
117- val filter = DynamicTagReader (reader, elementXmlDescriptor)
118-
119- // The test element can now be decoded as normal (with the filter applied)
120- val testElement = xml.decodeFromReader(elementSerializer, filter)
121- dataList.add(testElement)
122- } else { // handling unexpected tags
123- xml.config.unknownChildHandler(reader, InputKind .Element , reader.name, listOf (" Test_??" ))
114+ EventType .START_ELEMENT -> {
115+ if (input.namespaceURI.isEmpty() && input.localName.startsWith(" Test_" )) {
116+ // When reading the child tag we use the DynamicTagReader to present normalized XML to the
117+ // deserializer for elements
118+ val filter = DynamicTagReader (input, elementXmlDescriptor)
119+
120+ // The test element can now be decoded as normal (with the filter applied)
121+ val testElement = xml.decodeFromReader(elementSerializer, filter)
122+ dataList.add(testElement)
123+ } else { // handling unexpected tags
124+ @OptIn(ExperimentalXmlUtilApi ::class )
125+ xml.config.policy.handleUnknownContentRecovering(
126+ input,
127+ InputKind .Element ,
128+ elementXmlDescriptor,
129+ input.name,
130+ (0 until elementXmlDescriptor.elementsCount).map {
131+ val e = elementXmlDescriptor.getElementDescriptor(it)
132+ PolyInfo (e.tagName, it, e)
133+ }
134+ )
135+ }
124136 }
125- else -> { // other content that shouldn't happen
137+
138+ else -> // other content that shouldn't happen
126139 throw XmlException (" Unexpected tag content" )
127- }
128140 }
129141 }
130142 }
131143 return Container (dataList)
132144 }
133145
134- override fun serialize (encoder : Encoder , value : Container ) {
135- if (encoder is XML .XmlOutput ) { // When we are using the xml format use the serializeDynamic method
136- return serializeDynamic(encoder, encoder.target, value.data)
137- } else { // Otherwise just manually do the encoding that would have been generated
138- encoder.encodeStructure(descriptor) {
139- encodeSerializableElement(descriptor, 0 , ListSerializer (elementSerializer), value.data)
140- }
141- }
142- }
143-
144- /* *
145- * This function provides the actual dynamic serialization
146- */
147- fun serializeDynamic (encoder : Encoder , target : XmlWriter , data : List <TestElement >) {
148- val xml = delegateFormat(encoder) // get the format for deserializing
146+ override fun serializeXML (encoder : Encoder , output : XmlWriter , value : Container , isValueChild : Boolean ) {
147+ val xml =
148+ (encoder as XML .XmlOutput ).delegateFormat() // This format keeps the settings from the outer serializer, this allows for
149+ // serializing the children
149150
150151 // We need the descriptor for the element. xmlDescriptor returns a rootDescriptor, so the actual descriptor is
151152 // its (only) child.
152153 val elementXmlDescriptor = xml.xmlDescriptor(elementSerializer).getElementDescriptor(0 )
153-
154154 encoder.encodeStructure(descriptor) { // create the structure (will write the tags of Container)
155- for (element in data) { // write each element
155+ for (element in value. data) { // write each element
156156 // We need a writer that does the renaming from the normal format to the dynamic format
157157 // It is passed the string of the id to add.
158- val writer = DynamicTagWriter (target , elementXmlDescriptor, element.id.toString())
158+ val writer = DynamicTagWriter (output , elementXmlDescriptor, element.id.toString())
159159
160160 // Normal delegate writing of the element
161161 xml.encodeToWriter(writer, elementSerializer, element)
162162 }
163163 }
164164 }
165165
166- // These functions abstract away getting the delegate format in improved or compat way.
167- abstract fun delegateFormat (decoder : Decoder ): XML
168- abstract fun delegateFormat (encoder : Encoder ): XML
169166}
170167```
171168
@@ -181,13 +178,11 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
181178 private var initDepth = reader.depth
182179 private val filterDepth: Int
183180 /* *
184- * We want to be safe so only handle the content at relative depth 0. The way that depth is determined
185- * means that the depth is the depth after the tag (and end tags are thus one level lower than the tag (and its
186- * content). We correct for that here.
181+ * We want to be safe so only handle the content at relative depth 0.
187182 */
188183 get() = when (eventType) {
189- EventType .END_ELEMENT -> delegate.depth - initDepth + 1
190- else -> delegate.depth - initDepth
184+ EventType .END_ELEMENT -> delegate.depth - initDepth
185+ else -> delegate.depth - initDepth
191186 }
192187
193188 /* *
@@ -226,9 +221,10 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
226221 */
227222 override fun getAttributeNamespace (index : Int ): String = when (filterDepth) {
228223 0 -> when (index) {
229- 0 -> idAttrName.namespaceURI
224+ 0 -> idAttrName.namespaceURI
230225 else -> super .getAttributeNamespace(index - 1 )
231226 }
227+
232228 else -> super .getAttributeNamespace(index)
233229 }
234230
@@ -238,9 +234,10 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
238234 */
239235 override fun getAttributePrefix (index : Int ): String = when (filterDepth) {
240236 0 -> when (index) {
241- 0 -> idAttrName.prefix
237+ 0 -> idAttrName.prefix
242238 else -> super .getAttributePrefix(index - 1 )
243239 }
240+
244241 else -> super .getAttributePrefix(index)
245242 }
246243
@@ -250,9 +247,10 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
250247 */
251248 override fun getAttributeLocalName (index : Int ): String = when (filterDepth) {
252249 0 -> when (index) {
253- 0 -> idAttrName.localPart
250+ 0 -> idAttrName.localPart
254251 else -> super .getAttributeLocalName(index - 1 )
255252 }
253+
256254 else -> super .getAttributeLocalName(index)
257255 }
258256
@@ -262,9 +260,10 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
262260 */
263261 override fun getAttributeValue (index : Int ): String = when (filterDepth) {
264262 0 -> when (index) {
265- 0 -> idValue
263+ 0 -> idValue
266264 else -> super .getAttributeValue(index - 1 )
267265 }
266+
268267 else -> super .getAttributeValue(index)
269268 }
270269
@@ -276,7 +275,8 @@ internal class DynamicTagReader(reader: XmlReader, descriptor: XmlDescriptor) :
276275 filterDepth == 0 &&
277276 (nsUri ? : " " ) == idAttrName.namespaceURI &&
278277 localName == idAttrName.localPart -> idValue
279- else -> super .getAttributeValue(nsUri, localName)
278+
279+ else -> super .getAttributeValue(nsUri, localName)
280280 }
281281
282282 /* *
@@ -440,4 +440,4 @@ private fun newExample(testElements: List<TestElement>) {
440440 println (" Deserialized container:\n $deserializedData " )
441441
442442}
443- ```
443+ ```
0 commit comments