Python dicttoxml生成XML转字符串截断如何获取完整XML
Python dicttoxml生成XML decode后节点带
...截断问题解决方案 问题背景
在Python中使用dicttoxml库生成XML文件时,核心生成代码如下:
xml = dicttoxml(obj, item_func = my_item_func, attr_type=False, custom_root='Benefits')
该接口返回bytes类型的XML内容,为完成XSD校验,最初使用如下代码将其转换为字符串:
strxml = bytes.decode(xml, 'utf-8')
问题现象
转换得到的XML字符串存在大量节点被省略、替换为...的截断问题,最初推测触发原因是XML文件体积较大,但业务场景需要获取完整无省略的XML内容用于后续校验处理。
经测试,直接在浏览器渲染该xml bytes对象、IDE调试模式下打印该对象时,均可得到完整无截断的XML内容,仅在代码中执行decode转字符串、捕获print输出到变量时出现截断。
完整复现代码
from datetime import date from dicttoxml import dicttoxml from xml.dom.minidom import parseString import json import xmlschema def response_handler(self,node, mktsegtype): isvalidxml = False if node is not None: obj = json.loads(json.dumps(node,default=lambda o: dict((key, value) for key, value in o.__dict__.items() if value is not None),indent=4,allow_nan=False)) my_item_func = lambda x: 'cvrg' if x == "Covered" else('Insure' if x == "Insurance" else x[:-1]) xml = dicttoxml(obj, item_func = my_item_func, attr_type=False, custom_root='Benefits') isvalidxml = self.validatexmlwithxsd(xml, mktsegtype) if(isvalidxml): return xml else: return None def validatexmlwithxsd(self, xml, mktsegtype): valid = False xsd = None strxml = bytes.decode(xml, 'utf-8') if(mktsegtype == "XXX"): xsd = xmlschema.XMLSchema('tests/test_cases/examples/vehicles/vehicles.xsd') elif(mktsegtype == "YYY"): xsd = xmlschema.XMLSchema('tests/test_cases/examples/vehicles/boat.xsd') valid = xsd.is_valid(strxml) return valid
截断效果示例
生成的XML节点存在如下截断情况:
<cvrg> <cvrgID>285</cvrgID> <cvrgCoveredRtl>1</cvrgCoveredRtl> <cvrgCoveredMail>1</cvrgCoveredMail> <cvrgO...goryAgeLimitMax> </cvrgAgeLimitMax> <cvrgOutOfLimitActionAge></cvrgOutOfLimitActionAge> <cvrgOutOfLimitActionGender></cvrgOutOfLimitActionGender> </cvrg> <cvrg> <cvrgID>559</cvrgID> <cvrgCoveredRtl>2</cvrgCoveredRtl> <cvrgCoveredMail>2</cvrgCoveredMail> <cvrgOutOfLimitActionAge></cvrgOutOfLimitActionAge> <cvrgOutOfLimitActionGender></cvrgOutOfLimitActionGender> </cvrg>
已尝试无效方案
参考教程尝试将print输出捕获到变量中,代码如下:
from io import StringIO s = StringIO() print(xml, file=s,flush=True) result = s.getvalue()
该方案得到的XML仍然存在带...的节点截断问题,与直接decode的结果一致。目前仅调试模式、浏览器渲染可拿到完整XML,代码中获取字符串时始终截断。
问题根因
这个截断和UTF-8 decode逻辑、XML体积大小没有任何关系,问题出在传入dicttoxml的原始obj数据上:
序列化自定义类实例时用的default方法只过滤了None值,没有处理对象属性里的递归自引用、循环引用。dicttoxml遍历字典遇到循环引用时,会自动把重复引用的片段替换为...做截断。而IDE调试视图、浏览器渲染bytes内容时会自动做引用解引用,展示的是实际遍历生成的完整节点内容,才会出现"调试看是全的,代码里转字符串就缺内容"的假象。
可行解决方案
- 先彻底清洗传入dicttoxml的对象,在序列化阶段就阻断循环引用,不要把带自引用的对象传给dicttoxml,改造序列化逻辑增加循环引用检测:
def safe_serialize(obj, seen=None): if seen is None: seen = set() obj_id = id(obj) if obj_id in seen: return None seen.add(obj_id) if hasattr(obj, '__dict__'): res = {} for k, v in obj.__dict__.items(): if v is None: continue res[k] = safe_serialize(v, seen.copy()) return res elif isinstance(obj, (list, tuple)): return [safe_serialize(i, seen.copy()) for i in obj if i is not None] elif isinstance(obj, dict): return {k: safe_serialize(v, seen.copy()) for k, v in obj.items() if v is not None} else: return obj # 替换原来的obj生成逻辑 obj = safe_serialize(node)
- 跳过手动转字符串的步骤,xmlschema本身支持直接传入bytes类型对象做校验,避免中间环节的隐式截断:
# validatexmlwithxsd方法里直接传bytes对象给is_valid valid = xsd.is_valid(xml)
- 如果需要格式化XML字符串用于日志输出,用minidom的parseString直接解析bytes对象后再输出,不要走手动decode逻辑:
dom = parseString(xml) full_strxml = dom.toprettyxml(indent=" ", encoding='utf-8').decode('utf-8')
按以上步骤处理后,拿到的XML字符串就不会再出现...截断的问题,可以正常通过XSD校验。
内容的提问来源于stack exchange,提问作者Ratkiia
相关产品推荐
相关产品推荐

