Python 使用xmltodict从XML为有时重复的元素生成一致的JSON

Python 使用xmltodict从XML为有时重复的元素生成一致的JSON,python,xsd,lxml,xmltodict,python-xmlschema,Python,Xsd,Lxml,Xmltodict,Python Xmlschema,为了使用python将XML转换为JSON,存在几种方法,它们通常归结为相同的原则。今天最常用的方法是xmltodict 我试图将多个xml文档转换为JSON,但如果在某些文档中重复的元素只出现一次,就会遇到问题:在这种情况下,生成的不是列表而是对象。这种不一致性导致在例如spark中摄取JSON时出现问题,spark无法再推断JSON元素的模式(该元素是列表还是字符串?)并将其转换为字符串。 我们怎样才能最好地避开这件事?理想情况下,解析器将采用模式(XSD)并“了解”元素可以是无界的,因此应

为了使用python将XML转换为JSON,存在几种方法,它们通常归结为相同的原则。今天最常用的方法是xmltodict

我试图将多个xml文档转换为JSON,但如果在某些文档中重复的元素只出现一次,就会遇到问题:在这种情况下,生成的不是列表而是对象。这种不一致性导致在例如spark中摄取JSON时出现问题,spark无法再推断JSON元素的模式(该元素是列表还是字符串?)并将其转换为字符串。 我们怎样才能最好地避开这件事?理想情况下,解析器将采用模式(XSD)并“了解”元素可以是无界的,因此应该是列表,即使它们是单独的

  • 有没有使用XSD将XML转换为JSON的方法
  • 是否有其他可行的解决办法?例如,我正在考虑将每个非列表项放入一个列表中,但这将产生开销
  • 下面是一些显示问题的代码

    import xmltodict
    from lxml import etree
    
    xml1="""<?xml version="1.0" encoding="UTF-8"?>
    <a xmlns="https://some.com/ns" xsi:schemaLocation="https://some.com/ns schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><b>x</b></a>
    """
    xml2="""<?xml version="1.0" encoding="UTF-8"?>
    <a xmlns="https://some.com/ns" xsi:schemaLocation="https://some.com/ns schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><b>x</b><b>y</b></a>
    """
    xsd="""<?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="https://some.com/ns" targetNamespace="https://some.com/ns"  elementFormDefault="qualified" attributeFormDefault="qualified" version="2.4">
        <xs:element name="a" type="aType"/>
        <xs:complexType name="aType">
            <xs:sequence>
                <xs:element name="b" type="xs:string" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:schema>    
    """
    
    print(json.dumps(xmltodict.parse(xml1),indent=True))
    print(json.dumps(xmltodict.parse(xml2),indent=True))
    
    xmlschema_doc = etree.fromstring(xsd.encode())
    xmlschema = etree.XMLSchema(xmlschema_doc)
    
    
    xml1_doc = etree.fromstring(xml1.encode())
    print(etree.tostring(xml1_doc))
    result1 = xmlschema.validate(xml1_doc)
    print("validation xml1: ")
    print(result1)
    
    xml2_doc = etree.fromstring(xml2.encode())
    result2 = xmlschema.validate(xml2_doc)
    print("validation xml2: ")
    print(result2)
    
    对于xml2

    {
     "a": {
      "@xmlns": "https://some.com/ns",
      "@xsi:schemaLocation": "https://some.com/ns schema.xsd",
      "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
      "b": [
       "x",
       "y"
      ]
     }
    }
    
    解决方法可能是:

    def nestit(path, key, value):
        # don't nest lists. also dont nest attributes.
        if type(value) == list or key[:1]=='@':
            return key, value
        else:
            return key, [value]
    
    print(json.dumps(xmltodict.parse(xml1,postprocessor=nestit),indent=True))
    
    由此产生:

    {
     "a": [
      {
       "@xmlns": "https://some.com/ns",
       "@xsi:schemaLocation": "https://some.com/ns schema.xsd",
       "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
       "b": [
        "x"
       ]
      }
     ]
    }
    

    但是在一个非常复杂的xml结构中,对于从不重复的元素,效率会更低。

    有没有一种方法可以使用XSD将xml转换为JSON

    确实如此:

    import xmlschema
    
    schema = xmlschema.XMLSchema(xsd)
    print(json.dumps(xmlschema.to_dict(xml1,schema=schema,preserve_root=True),indent=True))
    
    返回:

    {
     "a": {
      "@xmlns": "https://some.com/ns",
      "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
      "@xsi:schemaLocation": "https://some.com/ns schema.xsd",
      "b": [
       "x"
      ]
     }
    }
    
    {
     "a": {
      "@xmlns": "https://some.com/ns",
      "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
      "@xsi:schemaLocation": "https://some.com/ns schema.xsd",
      "b": [
       "x"
      ]
     }
    }