赞同 5
分享

python转换复杂类型dict为xml

简介:最近有一个需求是将python的dict数据转换为xml,就这么一个简单的需求我竟然在常见的三方包里没有找到能达到我想要的效果的工具,和我一起复盘一下吧!
  2022.08.29
  Bug Man
  5
  48
  172.17.0.1
  中国.上海
 
 

这个需求是在我们对接一个外部平台的时候出现的,我们需要对一个C#后台和SQLServer数据库的项目进行移植。这个项目是一个问卷平台,数据操作的部分都在SQLServer的“存储过程”中,后台只需要将请求数据序列化为XML的格式,然后将XML作为参数去调用存储过程就可以了,在拿到结果数据之后再将数据序列化为json格式返回给前端。

在这一次迁移后台的过程中遇到了很多问题并解决了,说起来其实都是小问题,比如:配置django数据库orm使用的引擎(借用navicat依赖)、以游标对象操作数据库pymssql引擎的配置、数据中文编码问题(cp936)、游标对象结果dict处理(as_dict)、存储过程多结果集获取、选择适应pymssql引擎的django版本(django2.1)等,像这些问题只要能够做到仔细阅读官方文档就可以解决。我在这个过程中比较急躁,总觉得阅读官方文档虽然可以更好了解接口的使用,但想要解决所有问题可能是要通读整个文档,在时间紧任务急不了解相关技术栈的情况下摸个大致的框架更有利于完成任务,需要长期使用的技术栈则后期填补。

说回XML类型的转换问题,其实Python做dict转XML的工具有很多,但能够做到像C#中JosnHander.XMLSerializer这么严格转换的工具我确实没有找到,能够找到的还都是一些简单dict转XML、list转XML、转完list[dict]内层标签名叫items等。没有一个能够满足我现在需求的工具,于是我就使用标准库xml提供的Element对象和SubElement自己写了一个转换的程序,代码和实现的效果都贴在下方。

其实这就是编程语言的世界观决定了编程语言的方法论,用常见的Python方法转换XML其实也没有问题,只是遇到了一个强类型的语言,连list中的元素也是一个不会丢失类型的对象。

from tkinter.messagebox import NO
from xml.etree import ElementTree as ET
from xml.dom import minidom


class CollectionConvertToXml:

    @classmethod  
    def get_xml_string(cls, element, default_encoding='utf-8'):
        try:  
            rough_string = ET.tostring(element, default_encoding)  
            reparsed = minidom.parseString(rough_string)  
            return reparsed.toprettyxml(indent="  " , encoding=default_encoding)  
        except Exception as e:  
            print("getXml_string:传入的节点不能正确转换为xml,请检查传入的节点是否正确")
            return ""

    @classmethod  
    def process_list(cls, data, ref):
        sub_element = ET.SubElement(ref, data.__class__.__name__)
        for item in data:
            if isinstance(item, list):
                result = cls.process_list(item)
            elif isinstance(item, dict):
                result = cls.process_dict(item)
                sub_element.append(result)
            else:
                # 以下是字符串
                if item is None:
                    continue
                k_ele = ET.Element(item)
                k_ele.text = item
                sub_element.append(k_ele)
        return sub_element

    @classmethod
    def process_dict(cls, data):
        # 进来首先创建一个同名的标签
        element = ET.Element(data.__class__.__name__)
        for k, v in data.items():
            if isinstance(v, list):
                result_list = cls.process_list(v, element)
            elif isinstance(v, dict):
                result = cls.process_dict(v)
                element.append(result)
            else:
                # 以下是字符串
                if v is None:
                    continue
                k_ele = ET.Element(k)
                k_ele.text = str(v)
                element.append(k_ele)
        return element

    @classmethod
    def main(cls, data):
        """"""
        element = ""
        if isinstance(data, list):
            element = cls.process_list(data)
        elif isinstance(data, dict):
            element = cls.process_dict(data)
        return element



class CustomDict(dict):
    __setattr__ = dict.__setitem__
    __getattr__ = dict.__getitem__

    @property
    def __dict__(self):
        return {k: v for k, v in self.items()}


class usp_react_crf_mb_setup_save(CustomDict):
    pass


class CRF_MB_XXK(CustomDict):
    pass


class CRF_MB_FZK(CustomDict):
    pass


class CRF_MB_FZKList(list):
    pass


if __name__ == "__main__":
    crf_mb_fzk = {"fz_id": "b2c90c09-1044-40d0-51b8-47966cca5f22",
      "fz_title": "未命名",
      "fz_description": "",
      "fz_imges_description": "",
      "fz_display_order": 1,
      "fz_display_style": "1",
      "fz_questions_count": 1,
      "mb_id": "193FEF1E-99D8-4188-A524-6A0B75D21721"}
    crf_mb_fzklist = [CRF_MB_FZK(crf_mb_fzk)]
    crf_mb_xxk = {"mb_id": "193FEF1E-99D8-4188-A524-6A0B75D21721",
                "mb_creator_name": "",
                "mb_creator_id": "",
                "mb_create_time": "2022-08-26 16:17:28",
                "mb_hospital_source": "",
                "mb_disease_type_id": "",
                "mb_disease_type_name": "",
                "mb_status": "已创建",
                "mb_title": "测试创建模板",
                "mb_description": "后端测试1",
                "mb_imges_description": "uploadImg\\1661525971081_238379.jpg"}
    new_dict = {"CRF_MB_XXK": CRF_MB_XXK(crf_mb_xxk), "CRF_MB_FZKList": CRF_MB_FZKList(crf_mb_fzklist)}
    personList=usp_react_crf_mb_setup_save(new_dict)
    xml = CollectionConvertToXml.main(personList)
    pretty_xml = CollectionConvertToXml.get_xml_string(xml, default_encoding="utf-16")
    print(pretty_xml.decode("utf-16"))
    # <?xml version="1.0" encoding="utf-16"?>
    # <usp_react_crf_mb_setup_save>
    # <CRF_MB_XXK>
    #     <mb_id>193FEF1E-99D8-4188-A524-6A0B75D21721</mb_id>
    #     <mb_creator_name/>
    #     <mb_creator_id/>
    #     <mb_create_time>2022-08-26 16:17:28</mb_create_time>
    #     <mb_hospital_source/>
    #     <mb_disease_type_id/>
    #     <mb_disease_type_name/>
    #     <mb_status>已创建</mb_status>
    #     <mb_title>测试创建模板</mb_title>
    #     <mb_description>后端测试1</mb_description>
    #     <mb_imges_description>uploadImg\1661525971081_238379.jpg</mb_imges_description>
    # </CRF_MB_XXK>
    # <CRF_MB_FZKList>
    #     <CRF_MB_FZK>
    #     <fz_id>b2c90c09-1044-40d0-51b8-47966cca5f22</fz_id>
    #     <fz_title>未命名</fz_title>
    #     <fz_description/>
    #     <fz_imges_description/>
    #     <fz_display_order>1</fz_display_order>
    #     <fz_display_style>1</fz_display_style>
    #     <fz_questions_count>1</fz_questions_count>
    #     <mb_id>193FEF1E-99D8-4188-A524-6A0B75D21721</mb_id>
    #     </CRF_MB_FZK>
    # </CRF_MB_FZKList>
    # </usp_react_crf_mb_setup_save>