Repository: tefra/xsdata

Issue:
Lxmltreeserializer str versus list
I am trying out #975.

```py
import glob

from xsdata.formats.dataclass.context import XmlContext
from xsdata.formats.dataclass.parsers import XmlParser
from xsdata.formats.dataclass.parsers.config import ParserConfig
from xsdata.formats.dataclass.parsers.handlers import LxmlEventHandler, lxml
from xsdata.formats.dataclass.serializers import LxmlTreeSerializer

from netex import ServiceFrame

def conversion(input_filename: str, output_filename: str):
    context = XmlContext()
    config = ParserConfig(fail_on_unknown_properties=False)
    parser = XmlParser(context=context, config=config, handler=LxmlEventHandler)
    tree = lxml.etree.parse(input_filename)

    service_frame: ServiceFrame
    service_frame = parser.parse(tree.find(".//{http://www.netex.org.uk/netex}ServiceFrame"), ServiceFrame)

    lxml_serializer = LxmlTreeSerializer()
    element = tree.find(".//{http://www.netex.org.uk/netex}ServiceFrame")
    element.getparent().replace(element, lxml_serializer.render(service_frame))

    tree.write(output_filename, pretty_print=True, strip_text=True)

if __name__ == '__main__':
    for input_filename in glob.glob("/tmp/NeTEx_WSF_WSF_20240415_20240415.xml.gz"):
        print(input_filename)
        output_filename = input_filename.replace('/tmp/', 'netex-output-epip/')
        conversion(input_filename, output_filename)
```

The writing fails on the lxml_serializer.render, which complains with:
```
Traceback (most recent call last):
  File "/home/skinkie/Sources/reference/gtfs-netex-test/test-lxml.py", line 36, in <module>
    conversion(input_filename, output_filename)
  File "/home/skinkie/Sources/reference/gtfs-netex-test/test-lxml.py", line 28, in conversion
    element.getparent().replace(element, lxml_serializer.render(service_frame))
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/skinkie/Sources/reference/venv/lib/python3.11/site-packages/xsdata/formats/dataclass/serializers/tree/lxml.py", line 21, in render
    self.build(obj, builder)
  File "/home/skinkie/Sources/reference/venv/lib/python3.11/site-packages/xsdata/formats/dataclass/serializers/tree/mixins.py", line 55, in build
    builder.end(*element)
  File "src/lxml/saxparser.pxi", line 848, in lxml.etree.TreeBuilder.end
  File "src/lxml/saxparser.pxi", line 780, in lxml.etree.TreeBuilder._handleSaxEnd
  File "src/lxml/saxparser.pxi", line 749, in lxml.etree.TreeBuilder._flush
TypeError: sequence item 0: expected str instance, list found
```

If it can parse the code it, must be able to write it right?

With XmlTreeSerializer serialisation works.

File can be downloaded from: https://data.ndovloket.nl/netex/wsf/NeTEx_WSF_WSF_20240415_20240415.xml.gz

I have also found other bugs with rendering via lxmltreeserializer, where some elements just don't get rendered, their element name is added, but for example not their attributes.

Discussion:
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=1022) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=1022&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=1022&metric=new_accepted_issues&view=list)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=1022&resolved=false&inNewCodePeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=1022&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=1022)



Patch:
diff --git a/xsdata/formats/dataclass/serializers/tree/mixins.py b/xsdata/formats/dataclass/serializers/tree/mixins.py
index d1fa703c..488ba717 100644
--- a/xsdata/formats/dataclass/serializers/tree/mixins.py
+++ b/xsdata/formats/dataclass/serializers/tree/mixins.py
@@ -1,8 +1,9 @@
 import abc
 from dataclasses import dataclass
-from typing import Any, Dict, Protocol
+from typing import Any, Dict, Optional, Protocol
 
 from xsdata.exceptions import XmlHandlerError
+from xsdata.formats.converter import converter
 from xsdata.formats.dataclass.serializers.mixins import EventGenerator, XmlWriterEvent
 from xsdata.formats.types import T
 from xsdata.models.enums import EventType
@@ -50,10 +51,28 @@ class TreeSerializer(EventGenerator):
             elif event == XmlWriterEvent.ATTR:
                 key, value = element
                 pending_attrs[key] = value
-
             elif event == EventType.END:
                 builder.end(*element)
             elif event == XmlWriterEvent.DATA:
-                builder.data(*element)
+                data = self.encode_data(element[0])
+                builder.data(data)
             else:
                 raise XmlHandlerError(f"Unhandled event: `{event}`.")
+
+    @classmethod
+    def encode_data(cls, data: Any) -> str:
+        """Encode data for xml rendering.
+
+        Args:
+            data: The content to encode/serialize
+
+        Returns:
+            The xml encoded data
+        """
+        if data is None or isinstance(data, list) and not data:
+            return ""
+
+        if isinstance(data, str):
+            return data
+
+        return converter.serialize(data)


Test patch:
diff --git a/tests/formats/dataclass/serializers/tree/test_mixins.py b/tests/formats/dataclass/serializers/tree/test_mixins.py
index eab86c50..f01b80b5 100644
--- a/tests/formats/dataclass/serializers/tree/test_mixins.py
+++ b/tests/formats/dataclass/serializers/tree/test_mixins.py
@@ -19,3 +19,9 @@ class TreeSerializerTests(TestCase):
             serializer.build(books, builder)
 
         self.assertEqual("Unhandled event: `foobar`.", str(cm.exception))
+
+    def test_encode_data(self):
+        self.assertEqual("", TreeSerializer.encode_data(None))
+        self.assertEqual("", TreeSerializer.encode_data([]))
+        self.assertEqual("", TreeSerializer.encode_data(""))
+        self.assertEqual("1 2 3", TreeSerializer.encode_data([1, 2, 3]))