202

ウェブ上で不格好なXML->JSONコードのかなりの部分を見て、Stackのユーザーと少し対話したことで、この群衆はGoogleの結果の最初の数ページよりも多くのことを助けることができると確信しています。

そのため、天気フィードを解析しており、多数のWebサイトに天気ウィジェットを配置する必要があります。現在、Pythonベースのソリューションを検討しています。

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

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

4

20 に答える 20

334

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"]}}'
于 2012-04-18T01:06:05.353 に答える
66

XML と JSON の間には "1 対 1" のマッピングはありません。そのため、一方を他方に変換するには、必然的に、結果に対して何をしたいのかをある程度理解する必要があります。

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

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

于 2008-10-10T14:34:55.580 に答える
31

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>]

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

于 2015-09-20T07:37:54.507 に答える
15

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

import xmltodict

data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json 
于 2018-05-12T08:17:18.023 に答える
9

これが私がそのために構築したコードです。コンテンツの解析はなく、単純な変換のみです。

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()
于 2012-04-19T15:35:21.540 に答える
5

直接変換しないことをお勧めします。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 ):
    ...
于 2012-04-18T01:24:21.060 に答える
5

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

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

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

あなたが私に尋ねると、属性は最初のハックです。その後も、HTML でうまく機能しました。

于 2010-10-07T18:50:24.333 に答える
4

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

于 2008-10-10T14:30:59.193 に答える
2

XML解析用の組み込みライブラリは非常に優れていますが、私はlxmlに部分的です。

ただし、RSSフィードの解析には、Atomも解析できるUniversalFeedParserをお勧めします。その主な利点は、ほとんどの奇形の飼料でも消化できることです。

Python 2.6にはすでにJSONパーサーが含まれていますが、速度が向上した新しいバージョンがsimplejsonとして利用できます。

これらのツールを使用すると、アプリを構築するのはそれほど難しくありません。

于 2008-10-10T18:51:42.380 に答える
2

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

アプローチ

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

これにより、この場合に最適なアプローチが見つかりました。 を使用して xml を解析しlxml、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)

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

この部分のベンチマークは行っていません。

于 2016-10-20T10:38:21.313 に答える
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 解析でそれを行うには、データが異なるため、1 対 1 の解決策はありません。私の提案は、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'
于 2012-04-04T13:06:42.230 に答える
1

jsonpickleまたは feedparser を使用している場合は、feed_pa​​rser_to_json.py を試すことができます

于 2010-12-09T06:27:59.050 に答える