私はWebでXML-> JSONコードのかなりの部分をかなり見ましたが、Stackのユーザーと少し対話したことがあるので、この群衆はGoogleの結果の最初の数ページよりも多くのことを助けることができると確信しています。
そのため、天気フィードを解析し、多数のWebサイトに天気ウィジェットを追加する必要があります。現在、Pythonベースのソリューションを検討しています。
この公開 weather.com RSSフィード は、解析対象の良い例です(実際のweather.comフィードには、w/themとのパートナーシップのために追加情報が含まれています)。
簡単に言えば、Pythonを使用してXMLをJSONに変換する方法を教えてください。
XMLとJSONの間に「1対1」のマッピングはないため、一方を他方に変換するには、何をしたいかをある程度理解する必要がありますdo結果で。
そうは言っても、Pythonの標準ライブラリには XMLを解析するためのいくつかのモジュール (DOM、SAX、ElementTreeを含む)があります。 Python 2.6では、Pythonデータ構造とJSONの変換のサポートが json
モジュール に含まれています。
インフラストラクチャはそこにあります。
xmltodict (完全開示:私が書いた)は、XMLをdict + list + string構造に変換するのに役立ちます。これは "standard" に従います。 Expat -basedなので、非常に高速であり、XMLツリー全体をメモリにロードする必要はありません。
そのデータ構造を取得したら、それをJSONにシリアル化できます。
import xmltodict, json
o = xmltodict.parse('<e> <a>text</a> <a>text</a> </e>')
json.dumps(o) # '{"e": {"a": ["text", "text"]}}'
xmljson ライブラリを使用して、さまざまな XML JSON規則 を使用して変換できます。
たとえば、次のXML:
<p id="1">text</p>
BadgerFish規約 を介して次のように変換します。
{
'p': {
'@id': 1,
'$': 'text'
}
}
そして、 GData規約 を介して(属性はサポートされていません):
{
'p': {
'$t': 'text'
}
}
...そして Parker規約 を介して、この中に(属性はサポートされていません):
{
'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>]
開示:このライブラリを書きました。それが将来の検索者に役立つことを願っています。
XMLベースのマークアップをJSONとして転送する方法があります。これにより、元の形式にロスレスで変換できます。 http://jsonml.org/ を参照してください。
JSONの一種のXSLTです。お役に立てば幸いです
そのために私が作成したコードを次に示します。内容の解析はなく、単なる変換です。
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 __== '__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()
すべてのデータではなく応答コードのみを取得する場合、json parseのようなエラーが存在するため、textとして変換する必要があります
import xmltodict
data = requests.get(url)
xpars = xmltodict.parse(data.text)
json = json.dumps(xpars)
print json
http://designtheory.org/library/extrep/designdb-1.0.pdf をご覧ください。このプロジェクトは、XMLファイルの大規模なライブラリのXMLからJSONへの変換から始まります。変換では多くの研究が行われ、最も単純で直感的なXML-> JSONマッピングが作成されました(ドキュメントの初期段階で説明されています)。要約すると、すべてをJSONオブジェクトに変換し、繰り返しブロックをオブジェクトのリストとして配置します。
キー/値ペアを意味するオブジェクト(Pythonの辞書、Javaのハッシュマップ、JavaScriptのオブジェクト)
同じドキュメントを取得するためのXMLへのマッピングはありません。その理由は、キー/値のペアが属性であるか<key>value</key>
であるかが不明であるため、その情報が失われるためです。
あなたが私に尋ねると、属性は開始するハックです。その後、再びHTMLでうまく機能しました。
おそらく、おそらく最も簡単な方法は、XMLを辞書に解析し、それをsimplejsonでシリアル化することです。
直接のコンバージョンには進まないことをお勧めします。 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 ):
...
まだこれが必要な人には。この変換を行うための新しい、シンプルなコードを次に示します。
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
単純な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'
XML解析用の組み込みライブラリは非常に優れていますが、私は lxml の一部です。
しかし、RSSフィードの解析には niversal Feed Parser をお勧めします。これはAtomも解析できます。その主な利点は、ほとんどの不正なフィードでも消化できることです。
Python 2.6にはすでにJSONパーサーが含まれていますが、新しい 速度が改善されたバージョン は simplejson として利用可能です。
これらのツールを使用してアプリを作成するのはそれほど難しくありません。
pythonでXMLを使用して何かをするときは、ほとんど常にlxmlパッケージを使用します。ほとんどの人がlxmlを使用していると思います。 xmltodictを使用することもできますが、XMLを再度解析するというペナルティを支払う必要があります。
LxmlでXMLを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 __== "__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)
組み込みのmainからの出力は次のとおりです。
{
"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>
ここにあるものは積極的に維持されており、これまでのところ私のお気に入りです: xml2json in python
Declxmlを使用できます。マルチ属性や複雑なネストサポートなどの高度な機能があります。単純なプロセッサを作成するだけです。また、同じコードを使用して、JSONに戻すこともできます。それはかなり簡単で、ドキュメントは素晴らしいです。
lxml2jsonをチェックしてください(開示:書きました)
https://github.com/rparelius/lxml2json
非常に高速で軽量(lxmlのみが必要)であり、1つの利点は、特定の要素をリストまたは辞書に変換するかどうかを制御できることです。
jsonpickle またはfeedparserを使用している場合は、試すことができます feed_parser_to_json.py
私の答えは、xml全体をjsonに実際に変換する必要はない特定の(やや一般的な)ケースに対応していますが、必要なのはxmlの特定の部分をトラバース/アクセスすることです、fastおよびsimple(json/dictのような操作を使用)である必要があります。
このため、lxml
を使用してXMLをetreeに解析することは非常に高速であることに注意することが重要です。他のほとんどの答えの遅い部分は2番目のパスです。etree構造を(通常はpython-landで)走査し、jsonに変換します。
これは、この場合に私が最も見つけたアプローチにつながります:lxml
を使用してxmlを解析し、etreeノードを(怠lazに)ラップし、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を使用した場合と比較して、驚異的なstrikingの70倍(!)xmltodict そして、dictを直接走査します。
おまけに、構造はすでにdictに似ているため、xml2json
の別の代替実装を無料で入手できます。 dict-like構造体をjson.dumps
に渡すだけです。何かのようなもの:
def xml_to_json(xmlstr, **kwargs):
x = xml_to_dictlike(xmlstr, **kwargs)
return json.dumps(x)
Xmlに属性が含まれる場合、キーが有効なjsonキーであることを確認するために、英数字attr_prefix
(例:「ATTR_」)を使用する必要があります。
この部分のベンチマークは行っていません。