Pythonを使用してXMLをJSONに変換しますか?


170

Webで不自然なXML-> JSONコードがかなりの割合で見られ、Stackのユーザーと少しの間やり取りしてきたので、この群衆がGoogleの結果の最初の数ページよりも役立つことができると確信しています。

したがって、天気予報を解析し、多数のWebサイトに天気ウィジェットを入力する必要があります。現在、Pythonベースのソリューションを検討しています。

この公開のweather.com RSSフィードは、解析対象の良い例です(実際のweather.comフィードには、パートナーシップとの関係で追加情報が含まれています)。

簡単に言えば、Pythonを使用してXMLをJSONに変換するにはどうすればよいですか?

回答:


61

XMLとJSONの間に「1対1」のマッピングはないため、1つをもう1つに変換するに、結果をどのように処理するかをある程度理解する必要があります。

そうは言っても、Pythonの標準ライブラリには、XMLを解析するためのいくつかのモジュール(DOM、SAX、ElementTreeなど)があります。Python 2.6以降、jsonモジュールに Pythonデータ構造とJSON間の変換のサポートが含まれています

したがって、インフラストラクチャはそこにあります。


2
xmljson IMHOは、さまざまな規則をサポートしているため、すぐに使用できます。pypi.org/project/xmljson
nitinr708

新しい回答ではすでに言及されています。それでも、有効なXML構成要素の小さなサブセットしかカバーしていませんが、おそらく実際に使用されているものの大部分をカバーしています。
Dan Lenski 2018

281

xmltodict(完全な開示:私が作成しました)は、この「標準」に従って、XMLをdict + list + string構造に変換するのに役立ちます。それはExpatのは、それは非常に高速ですので、ベースとメモリ全体XMLツリーをロードする必要はありません。

データ構造を取得したら、それをJSONにシリアル化できます。

import xmltodict, json

o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'

@Martin Blech djangoモデルファイルからjsonファイルを作成した場合。xmlファイルをマップして、必須フィールドのxmlをjsonに変換するにはどうすればよいですか?
2014年

1
@saythこれを別のSO質問として投稿する必要があると思います。
Martin Blech、2014年

@Martin Blech。私は質問を追加しましたが、それはSOに収まるようにかなり難しいですが、私は私ができるように多くの情報として提供している初心者ですが、私はあなたがよりわかりやすくする必要があり期待stackoverflow.com/q/23676973/461887を
sayth

久しぶりに、一部のLinuxディストリビューションではxmltodictが「標準」ライブラリではないことに少し驚いています。私たちは読むことができるものから直接仕事をしているようですが、残念ながらxslt変換のような別のソリューションを使用します
sancelot

この素晴らしいライブラリを書いてくれてありがとう。bs4
口述

24

xmljsonライブラリを使用すると、さまざまなXML JSON規則を使用して変換できます。

たとえば、次のXML:

<p id="1">text</p>

BadgerFish規約を介してこれを次のように変換します。

{
  'p': {
    '@id': 1,
    '$': 'text'
  }
}

GData規約を介してこれに追加します(属性はサポートされていません):

{
  'p': {
    '$t': 'text'
  }
}

...そしてこれにパーカーの慣習を介して(属性はサポートされていません):

{
  'p': 'text'
}

同じ規則を使用して、XMLからJSONおよびJSONからXMLに変換することができます。

>>> import json, xmljson
>>> from lxml.etree import fromstring, tostring
>>> xml = fromstring('<p id="1">text</p>')
>>> json.dumps(xmljson.badgerfish.data(xml))
'{"p": {"@id": 1, "$": "text"}}'
>>> xmljson.parker.etree({'ul': {'li': [1, 2]}})
# Creates [<ul><li>1</li><li>2</li></ul>]

開示:このライブラリを作成しました。将来の検索に役立つことを願っています。


4
これはかなりクールなライブラリですが、個人のオープンソースライブラリを提供する方法を読んでくださいあなたがそれを誇示するより多くの答えを投稿する前に。
Martijn Pieters

1
@MartijnPietersに感謝-私はこれを経験したばかりであり、私がこれに固執することを確認します。
Sアナンド

1
ソリューションを提供してくれたAnandに感謝します。うまく機能しているようで、外部依存関係がなく、さまざまな規則を使用して属性を処理する方法に多くの柔軟性を提供します。まさに私が必要としていたものであり、私が見つけた最も柔軟で最も簡単なソリューションでした。
mbbeme 2016年

おかげで、Anand-残念ながら、utf8エンコーディングでXMLを解析できません。ソースを通過すると、XMLParser(..)を介したエンコーディングセットは無視されるようです
Patrik Beck

@PatrikBeck壊れるutf8エンコーディングを使用したXMLの小さな例を共有していただけませんか?
Sアナンド2017年

11

すべてのデータではなく応答コードしか取得しない場合は、json parseなどのエラーが発生するので、テキストとして変換する必要があります

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 

7

これが私が作成したコードです。内容の解析はなく、単純な変換のみです。

from xml.dom import minidom
import simplejson as json
def parse_element(element):
    dict_data = dict()
    if element.nodeType == element.TEXT_NODE:
        dict_data['data'] = element.data
    if element.nodeType not in [element.TEXT_NODE, element.DOCUMENT_NODE, 
                                element.DOCUMENT_TYPE_NODE]:
        for item in element.attributes.items():
            dict_data[item[0]] = item[1]
    if element.nodeType not in [element.TEXT_NODE, element.DOCUMENT_TYPE_NODE]:
        for child in element.childNodes:
            child_name, child_dict = parse_element(child)
            if child_name in dict_data:
                try:
                    dict_data[child_name].append(child_dict)
                except AttributeError:
                    dict_data[child_name] = [dict_data[child_name], child_dict]
            else:
                dict_data[child_name] = child_dict 
    return element.nodeName, dict_data

if __name__ == '__main__':
    dom = minidom.parse('data.xml')
    f = open('data.json', 'w')
    f.write(json.dumps(parse_element(dom), sort_keys=True, indent=4))
    f.close()

7

XMLベースのマークアップをJSONとして転送する方法があり、それを無損失で元の形式に戻すことができます。http://jsonml.org/を参照してください。

JSONの一種のXSLTです。お役に立てば幸いです


7

まだこれを必要とする可能性がある人には。これは、この変換を行うための新しいシンプルなコードです。

from xml.etree import ElementTree as ET

xml    = ET.parse('FILE_NAME.xml')
parsed = parseXmlToJson(xml)


def parseXmlToJson(xml):
  response = {}

  for child in list(xml):
    if len(list(child)) > 0:
      response[child.tag] = parseXmlToJson(child)
    else:
      response[child.tag] = child.text or ''

    # one-liner equivalent
    # response[child.tag] = parseXmlToJson(child) if len(list(child)) > 0 else child.text or ''

  return response

1
少なくともPython 3.7で機能しますが、残念ながら、特定の値がxmlにある場合、予期しないデータがキー名に追加されます。たとえば、ルートレベルノードのxmlnsタグは、次のようにすべてのノードキーに表示されます:{'{ maven .apache.org / POM / 4.0.0 } artifactId ':' test-service '、これは次のようなxmlから取得されます:<project xmlns = " maven.apache.org/POM/4.0.0 " xsi:schemaLocation = " maven .apache.org / POM / 4.0.0 maven.apache.org/xsd/maven-4.0.0.xsd "xmlns:xsi =" w3.org/2001/XMLSchema-instance "> <modelVersion> 4.0.0 </ modelVersion>
hrbdg

5

http://designtheory.org/library/extrep/designdb-1.0.pdfをご覧になることをお勧めします。このプロジェクトは、XMLファイルの大規模なライブラリのXMLからJSONへの変換から始まります。変換には多くの調査が行われ、最も単純で直感的なXML-> JSONマッピングが生成されました(これはドキュメントの初期に説明されています)。要約すると、すべてをJSONオブジェクトに変換し、繰り返しブロックをオブジェクトのリストとして配置します。

キーと値のペアを意味するオブジェクト(Pythonの辞書、Javaのハッシュマップ、JavaScriptのオブジェクト)

同一のドキュメントを取得するためのXMLへのマッピングはありません。理由は、キーと値のペアが属性であるかであるか不明<key>value</key>であるため、その情報は失われます。

私に尋ねると、属性はハックです。その後、再びHTMLでうまく機能しました。


4

まあ、おそらく最も簡単な方法は、XMLを解析して辞書を作成し、それをsimplejsonでシリアル化することです。


4

直接のコンバージョンはしないことをお勧めします。XMLをオブジェクトに変換し、次にオブジェクトからJSONに変換します。

私の意見では、これはXMLとJSONがどのように対応するかをより明確に定義します。

正しくなるには時間がかかり、その一部を生成するのに役立つツールを作成することもできますが、おおよそ次のようになります。

class Channel:
  def __init__(self)
    self.items = []
    self.title = ""

  def from_xml( self, xml_node ):
    self.title = xml_node.xpath("title/text()")[0]
    for x in xml_node.xpath("item"):
      item = Item()
      item.from_xml( x )
      self.items.append( item )

  def to_json( self ):
    retval = {}
    retval['title'] = title
    retval['items'] = []
    for x in items:
      retval.append( x.to_json() )
    return retval

class Item:
  def __init__(self):
    ...

  def from_xml( self, xml_node ):
    ...

  def to_json( self ):
    ...

2

単純なXMLの切り取りの場合、正規表現を使用すると問題を回避できます。例えば:

# <user><name>Happy Man</name>...</user>
import re
names = re.findall(r'<name>(\w+)<\/name>', xml_string)
# do some thing to names

@Danが言ったように、XML解析によってそれを行うには、データが異なるため、万能なソリューションはありません。私の提案はlxmlを使用することです。jsonまでは完了していませんが、lxml.objectifyは静かで良い結果をもたらします。

>>> from lxml import objectify
>>> root = objectify.fromstring("""
... <root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...   <a attr1="foo" attr2="bar">1</a>
...   <a>1.2</a>
...   <b>1</b>
...   <b>true</b>
...   <c>what?</c>
...   <d xsi:nil="true"/>
... </root>
... """)

>>> print(str(root))
root = None [ObjectifiedElement]
    a = 1 [IntElement]
      * attr1 = 'foo'
      * attr2 = 'bar'
    a = 1.2 [FloatElement]
    b = 1 [IntElement]
    b = True [BoolElement]
    c = 'what?' [StringElement]
    d = None [NoneElement]
      * xsi:nil = 'true'

1
ただし、重複するノードは削除されます
Pooya

2

XML構文解析用の組み込みライブラリーは非常に優れていますが、私はlxmlに不慣れです。

ただし、RSSフィードの解析には、Atomも解析できるUniversal Feed Parserをお勧めします。その主な利点は、ほとんどの不正なフィードでも消化できることです。

Python 2.6にはすでにJSONパーサーが含まれていますが、速度改善された新しいバージョンがsimplejsonとして入手できます。

これらのツールがあれば、アプリの構築はそれほど難しくありません。


2

私の答えは、xml全体をjsonに実際に変換する必要がない特定の(そして幾分一般的な)ケースに対処しますが、必要なのは、xmlの特定の部分をトラバース/アクセスすることであり、高速である必要があります。シンプル(json / dictのような操作を使用)。

アプローチ

このため、使用するxmlからetreeへの解析lxmlは非常に高速であることに注意することが重要です。他のほとんどの答えの遅い部分は2番目のパスです。etree構造をトラバースし(通常はpython-landで)、それをjsonに変換します。

これにより、この場合に最適とlxml思われるアプローチにつながります。つまり、を使用してxmlを解析し、etreeノードを(遅延的に)ラップして、dictのようなインターフェイスを提供します。

コード

これがコードです:

from collections import Mapping
import lxml.etree

class ETreeDictWrapper(Mapping):

    def __init__(self, elem, attr_prefix = '@', list_tags = ()):
        self.elem = elem
        self.attr_prefix = attr_prefix
        self.list_tags = list_tags

    def _wrap(self, e):
        if isinstance(e, basestring):
            return e
        if len(e) == 0 and len(e.attrib) == 0:
            return e.text
        return type(self)(
            e,
            attr_prefix = self.attr_prefix,
            list_tags = self.list_tags,
        )

    def __getitem__(self, key):
        if key.startswith(self.attr_prefix):
            return self.elem.attrib[key[len(self.attr_prefix):]]
        else:
            subelems = [ e for e in self.elem.iterchildren() if e.tag == key ]
            if len(subelems) > 1 or key in self.list_tags:
                return [ self._wrap(x) for x in subelems ]
            elif len(subelems) == 1:
                return self._wrap(subelems[0])
            else:
                raise KeyError(key)

    def __iter__(self):
        return iter(set( k.tag for k in self.elem) |
                    set( self.attr_prefix + k for k in self.elem.attrib ))

    def __len__(self):
        return len(self.elem) + len(self.elem.attrib)

    # defining __contains__ is not necessary, but improves speed
    def __contains__(self, key):
        if key.startswith(self.attr_prefix):
            return key[len(self.attr_prefix):] in self.elem.attrib
        else:
            return any( e.tag == key for e in self.elem.iterchildren() )


def xml_to_dictlike(xmlstr, attr_prefix = '@', list_tags = ()):
    t = lxml.etree.fromstring(xmlstr)
    return ETreeDictWrapper(
        t,
        attr_prefix = '@',
        list_tags = set(list_tags),
    )

この実装は完全ではありません。たとえば、要素にテキストと属性の両方、またはテキストと子の両方が含まれる場合を完全にはサポートしていません(作成したときに必要がなかったためです...)簡単なはずです。しかし、それを改善するために。

速度

XMLの特定の要素のみを処理する必要がある私の特定のユースケースでは、@ Martin Blechのxmltodictを使用してdictを直接トラバースする場合と比較して、このアプローチにより、70倍(!)の驚くべき驚くべき高速化が実現しました

ボーナス

おまけとして、私たちの構造はすでにdictのようなものなので、xml2json無料の別の代替実装を手に入れます。dictのような構造をに渡すだけですjson.dumps。何かのようなもの:

def xml_to_json(xmlstr, **kwargs):
    x = xml_to_dictlike(xmlstr, **kwargs)
    return json.dumps(x)

XMLに属性が含まれている場合attr_prefix、キーが有効なjsonキーであることを確認するために、英数字(「ATTR_」など)を使用する必要があります。

私はこの部分をベンチマークしていません。


実行しようとjson.dumps(tree)すると、タイプ 'ETreeDictWrapper'のオブジェクトはJSONシリアライズ可能ではありません
Vlad T.

2

PythonでXMLを使って何かをするとき、私はほとんど常にlxmlパッケージを使用します。ほとんどの人がlxmlを使用していると思います。xmltodictを使用することもできますが、XMLを再度解析するペナルティを支払う必要があります。

lxmlを使用してXMLをjsonに変換するには:

  1. lxmlを使用してXMLドキュメントを解析する
  2. lxmlをdictに変換する
  3. リストをjsonに変換

私のプロジェクトでは次のクラスを使用しています。toJsonメソッドを使用します。

from lxml import etree 
import json


class Element:
    '''
    Wrapper on the etree.Element class.  Extends functionality to output element
    as a dictionary.
    '''

    def __init__(self, element):
        '''
        :param: element a normal etree.Element instance
        '''
        self.element = element

    def toDict(self):
        '''
        Returns the element as a dictionary.  This includes all child elements.
        '''
        rval = {
            self.element.tag: {
                'attributes': dict(self.element.items()),
            },
        }
        for child in self.element:
            rval[self.element.tag].update(Element(child).toDict())
        return rval


class XmlDocument:
    '''
    Wraps lxml to provide:
        - cleaner access to some common lxml.etree functions
        - converter from XML to dict
        - converter from XML to json
    '''
    def __init__(self, xml = '<empty/>', filename=None):
        '''
        There are two ways to initialize the XmlDocument contents:
            - String
            - File

        You don't have to initialize the XmlDocument during instantiation
        though.  You can do it later with the 'set' method.  If you choose to
        initialize later XmlDocument will be initialized with "<empty/>".

        :param: xml Set this argument if you want to parse from a string.
        :param: filename Set this argument if you want to parse from a file.
        '''
        self.set(xml, filename) 

    def set(self, xml=None, filename=None):
        '''
        Use this to set or reset the contents of the XmlDocument.

        :param: xml Set this argument if you want to parse from a string.
        :param: filename Set this argument if you want to parse from a file.
        '''
        if filename is not None:
            self.tree = etree.parse(filename)
            self.root = self.tree.getroot()
        else:
            self.root = etree.fromstring(xml)
            self.tree = etree.ElementTree(self.root)


    def dump(self):
        etree.dump(self.root)

    def getXml(self):
        '''
        return document as a string
        '''
        return etree.tostring(self.root)

    def xpath(self, xpath):
        '''
        Return elements that match the given xpath.

        :param: xpath
        '''
        return self.tree.xpath(xpath);

    def nodes(self):
        '''
        Return all elements
        '''
        return self.root.iter('*')

    def toDict(self):
        '''
        Convert to a python dictionary
        '''
        return Element(self.root).toDict()

    def toJson(self, indent=None):
        '''
        Convert to JSON
        '''
        return json.dumps(self.toDict(), indent=indent)


if __name__ == "__main__":
    xml='''<system>
    <product>
        <demod>
            <frequency value='2.215' units='MHz'>
                <blah value='1'/>
            </frequency>
        </demod>
    </product>
</system>
'''
    doc = XmlDocument(xml)
    print doc.toJson(indent=4)

組み込みメインからの出力は次のとおりです。

{
    "system": {
        "attributes": {}, 
        "product": {
            "attributes": {}, 
            "demod": {
                "attributes": {}, 
                "frequency": {
                    "attributes": {
                        "units": "MHz", 
                        "value": "2.215"
                    }, 
                    "blah": {
                        "attributes": {
                            "value": "1"
                        }
                    }
                }
            }
        }
    }
}

これは、このxmlの変換です。

<system>
    <product>
        <demod>
            <frequency value='2.215' units='MHz'>
                <blah value='1'/>
            </frequency>
        </demod>
    </product>
</system>




1

declxmlを使用できます。マルチ属性や複雑なネストサポートなどの高度な機能があります。あなたはそれのために単純なプロセッサを書く必要があるだけです。また、同じコードを使用して、JSONに戻すこともできます。それはかなり簡単で、ドキュメントは素晴らしいです。

リンク:https : //declxml.readthedocs.io/en/latest/index.html


-1

Pythonでデータを準備する:最初にJSONを作成するには、Pythonでデータを準備する必要があります。Pythonでリストと辞書を使用してデータを準備できます。

Pythonリスト<==> JSON配列

Python辞書<==> JSONオブジェクト(キー値の形式)詳細については、こちらを確認してください

https://devstudioonline.com/article/create-json-and-xml-in-python


Stack Overflowへようこそ!リンクは知識を共有するための優れた方法ですが、将来リンクが壊れたとしても、質問に答えることはできません。質問に回答するリンクの重要なコンテンツを回答に追加します。コンテンツが複雑すぎて大きすぎてここに収まらない場合は、提案されたソリューションの一般的なアイデアを説明してください。元のソリューションのWebサイトへのリンク参照を常に保持することを忘れないでください。参照:良い回答を書くにはどうすればよいですか?
sɐunıɔןɐqɐp

-4

データをJSON形式で表すには

name=John
age=20
gender=male
address=Sector 12 Greater Kailash, New Delhi
Jobs=Noida,Developer | Gurugram,Tester |Faridabad,Designer

JSON我々は、キーと値の形式でデータをrepesent

{
    "name":"john",
    "age":20,
    "gender":"male",
    "address":["New kP college","Greater Kailash","New Delhi"],
    "jobs":[
               {"Place":"Noida","Title":"Developer "},
               {"Place":"Gurugram","Title":"Tester "},
               {"Place":"Faridabad","Title":"Designer"}
           ]
}

データをXML形式で表すには

<!-- In xml we write a code under a key you can take any key -->
<info> <!-- key open -->

<name> john </name> 
<age> 20 </age>
<gender> male </gender>

<address> 
<item> New kP college </item>
<item> Greater Kailash </item>
<item> New Delhi </item>
</address>

<jobs>
 <item>
  <title>Developer </title>
  <place>Noida</place>
 </item>

 <item>
  <title>Designer</title>
  <place>Gurugram</place>
 </item>
 
 <item>
  <title>Developer </title>
  <place>Faridabad</place>
 </item>
</jobs>

</info> <!-- key close-->

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.