クラス説明 Class Description
class XMLtoJSON( output=None, input=None, input_string=None, indent=2, output_file_append=False );
method parse()
method parse_string(string = None)
method parse_stream(stream = None)
method parse_file(path = None)
- 「output」はJSONのアウトプット先を制御します。
- 「input」はXMLのインプット先を制御します。
- 「input_string」はXMLのインプットを文字列で行う場合に使います。
- 'output' controls the direction of JSON output.
- 'input' controls the direction of XML input.
- 'input_string' controls the direction of XML input.
- 「output」が与えられなければ、parseメソッドの戻り値で文字列として帰ってきます。
- file/io_stream系のオブジェクトなら、そこに書き込まれます。
- 文字列が指定された場合は、その文字列が指し示すパスのファイルに書き込まれます。
- when 'output' is not given, the result will be returned by parse methods as string.
- otherwise, it is an io_stream object, the result will be written in the stream.
- and it is an string object, the result will be written in the file specified path of the string.
- 「input」が file/io_stream系のオブジェクトなら、そこから読み込まれます。
- 文字列が指定された場合は、その文字列が指し示すパスのファイルから読み出されます。
- 指定されていない場合は、「input_string」で指定された文字列が使われます。
- 両方指定された場合はinputが優先です。
- when input is an io_stream object, parser will read from there.
- otherwise, it is an string object, parser will read from the file specified path of the string.
- when it is not given parser will use string specified as 'input_string'.
- when both given, 'input' is given the priority.
- 「indent」はインデントの深さを制御します。
- デフォルトは2で、falseの場合はプリティプリント自体しません。
- 'indent' controls indent depth of result. default is 2.
- if set false, this will not do pretty printing.
- 「output_file_append」は出力先がファイルだった場合に、ファイルに追記するかを指定します
- 追記する場合は True 上書きする場合は False (デフォルト) です。
例 Example
p = XMLtoJSON( output=sys.stdout, input="./test.xml", indent=False )
p.parse()
- "./test.xml"を読み込み、パースし、sys.stdoutに書き出す。
- This means that, parse "./test.xml" and print result to sys.stdout with no indent.
p = XMLtoJSON( output="./test.json", input_string"<?xml version='1.0' encoding='UTF-8'?><abc version='0.1'></abc>" )
p.parse()
- "string"をパースし、"./test.json"に書き出す。
- "string" -> parse -> "./test.json"
p = XMLtoJSON( input_string="<?xml version='1.0' encoding='UTF-8'?><abc version='0.1'></abc>" )
print p.parse()
- "string"をパースし、parse()メソッドの戻り値で文字列として返す。
- "string" -> parse -> return as string.
p = XMLtoJSON( output="./test.json", output_file_append=True )
st = StringIO.StringIO()
st.write("<?xml version='1.0' encoding='UTF-8'?><abc version='0.1'><bcd>a</bcd>")
p.set_input(st)
st.write("<bcd>b</bcd></abc>")
p.parse()
- 「input」にStringIO.StringIOかio.StringIOを指定した場合、
- 指定した時点までに書き込まれている値のみパースされます。
- その後に書き込んだ値は無視されます。
- when you set 'input' to StringIO.StringIO io.StringIO
- The value that will be parsed is just the value that have already written by the time when set_input() called.
- Following writing will be ignored.
コード Code
import sys
import re
import xml.sax
import io
import StringIO
class XMLtoJSON_ContentHandler(xml.sax.handler.ContentHandler):
def __init__(self, output=sys.stdout, pretty_print=True, indent=2):
self.output = output
self.indent = indent
self.indent_space = ' '*self.indent
self.pretty_print = pretty_print
self.last_called = "__init__"
def startDocument(self):
self.data = {}
self.p_data = [ self.data ]
self.continuations = []
self.push_text_node = []
self.last_called = "startDocument"
def endDocument(self):
self.print_json()
def characters(self, content):
this_push_text_node = self.push_text_node[-1]
line = re.match('^\s*(.*)$', content)
if line and len(line.groups()[0]) > 0:
this_push_text_node(line.groups()[0], self.last_called == "characters")
self.last_called = "characters"
def startElement(self, name, attr):
this_p_data = self.p_data[-1]
this_data = { 'd': {} }
def this_push_text_node(node, continued_p):
data = this_data['d']
if not data.get('#text'):
data['#text'] = node
elif isinstance( (data['#text']) , list):
if continued_p:
data['#text'][-1] = data['#text'][-1] + '\\n' + node
else:
data['#text'].append(node)
else:
if continued_p:
data['#text'] = data['#text'] + '\\n' + node
else:
data['#text'] = [data['#text'], node]
self.push_text_node.append(this_push_text_node)
for key in attr.getNames():
value = attr.getValue(key)
this_data['d'][key] = value
def cont():
keys = this_data['d'].keys()
if keys == ['#text']:
if not isinstance( (this_data['d']['#text']) , list) :
this_data['d'] = this_data['d']['#text']
if keys:
if not this_p_data.get(name):
this_p_data[name] = this_data['d']
elif isinstance( (this_p_data[name]) , list) :
this_p_data[name].append(this_data['d'])
else:
this_p_data[name] = [this_p_data[name], this_data['d']]
self.continuations.append(cont)
self.p_data.append(this_data['d'])
self.last_called = "startElement"
def endElement(self, name):
self.p_data.pop()
self.push_text_node.pop()
cont = self.continuations.pop()
cont()
self.last_called = "endElement"
def print_json(self):
first_time = [1]
def it (h, nesting=0):
if isinstance(h, dict):
if self.pretty_print:
if not first_time[0]:
self.output.write ( "\n" )
else:
first_time[0] = 0
for i in range(nesting):
self.output.write (self.indent_space)
self.output.write ( "{" )
l, i = len(h), 0
for k, v in h.iteritems():
self.output.write ( '"' + k + '"' + ':' )
it(v, nesting+1)
if i < (l-1):
self.output.write ( "," )
if self.pretty_print:
self.output.write ( "\n " )
for j in range(nesting):
self.output.write (self.indent_space)
i+=1
self.output.write( "}" )
if isinstance(h, list):
self.output.write ( "[" )
l, i = len(h), 0
for a in h:
it(a, nesting+1)
if i < (l-1):
self.output.write(',')
i+=1
self.output.write ( "]" )
if isinstance (h, basestring):
h = h.replace('"', '\\"')
self.output.write ('"' + h + '"')
it(self.data)
if self.pretty_print:
self.output.write("\n")
class XMLtoJSON():
def __init__( self, output=None, input=None, input_string=None, indent=2, output_file_append=False ):
self.indent = indent
pretty_print = (self.indent != False) and (self.indent > -1);
self.handler = XMLtoJSON_ContentHandler( None,
pretty_print,
self.indent or 0 )
self.output_file_append = output_file_append
self.set_output(output)
if input != None:
self.set_input(input)
else:
self.set_input_string(input_string or "")
def set_output(self, output=None):
if output == None:
self.output_type = "s"
elif isinstance(output, (file, StringIO.StringIO, io.StringIO)):
self.output_type = "i"
self.handler.output = output
elif isinstance(output, (str, basestring)):
self.output_type = "f"
else:
raise TypeError("The specified 'output' type is not surported.")
self.output = output
def set_input(self, input):
if isinstance(input, file):
self.input_type = "i"
elif isinstance(input, (str, basestring)):
self.input_type = "f"
elif isinstance(input, (StringIO.StringIO, io.StringIO)):
self.set_input_string(input.getvalue())
return
else:
raise TypeError("The specified 'input' type is not surported.")
self.input = input
def set_input_string(self, input_string):
if isinstance(input_string, (str, basestring)):
self.input_type = "s"
else:
raise TypeError("The specified value is not a string.")
self.input_string = input_string
def parse_base(self, parsing):
if self.output_type == "s":
o = StringIO.StringIO()
self.handler.output = o
parsing()
try:
c = o.getvalue()
finally:
o.close()
return c
if self.output_type == "f":
if self.output_file_append:
mode = 'a'
else:
mode = 'w'
with file(self.output, mode) as f:
self.handler.output = f
parsing()
return
parsing()
def parse_string(self, string = None):
def parsing():
xml.sax.parseString((string or self.input_string), self.handler)
return self.parse_base(parsing)
def parse_stream(self, stream = None):
def parsing():
xml.sax.parse((stream or self.input), self.handler)
return self.parse_base(parsing)
def parse_file(self, path = None):
def parsing():
with file((path or self.input), 'r') as f:
xml.sax.parse(f, self.handler)
return self.parse_base(parsing)
def parse(self):
if(self.input_type == "s"):
return self.parse_string()
if(self.input_type == "i"):
return self.parse_stream()
if(self.input_type == "f"):
return self.parse_file()
else:
raise StandardError("input_type is unknown type. -> '"+self.input_type+"' .")
変換例 Translation Example
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items>
<item>
<jcity>Chiyoda-ku</jcity>
<jlocal>Chiyoda</jlocal>
<jpref>TOKYO</jpref>
<pref_cd>13</pref_cd>
<zip_cd>1000001</zip_cd>
</item>
</items>
{"items":
{"item":
{"jlocal":"Chiyoda",
"zip_cd":"1000001",
"jpref":"TOKYO",
"pref_cd":"13",
"jcity":"Chiyoda-ku"}}}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<children>
<girl>HANAKO</girl>
<boy>TARO</boy>
<boy>JIRO</boy>
</children>
{"children":
{"boy":["TARO","JIRO"],
"girl":"HANAKO"}}
<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel rdf:about="http://www.kawa.net/xp/index-j.html">
<title>Kawa.net xp - Ajax&Perl技術情報(川崎有亮)</title>
<link>http:
<dc:date>2010-10-31T20:57:00+09:00</dc:date>
<dc:language>ja</dc:language>
<dc:rights>Copyright 1995-2010 Yusuke Kawasaki. All rights reserved.</dc:rights>
<description>川崎有亮の制作したプログラムのご紹介・技術情報など。ajax/JavaScript/Perl/CGI/...</description>
<image rdf:resource="http://www.kawa.net/xp/images/xp-title-128x32.gif" />
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://kawa.at.webry.info/201101/article_3.html" />
<rdf:li rdf:resource="http://kawa.at.webry.info/201101/article_2.html" />
................................以下略
{"rdf:RDF":
{"xmlns:rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"xmlns":"http://purl.org/rss/1.0/",
"image":
{"url":"http://www.kawa.net/xp/images/xp-title-128x32.gif",
"link":"http://www.kawa.net/xp/index-j.html",
"rdf:about":"http://www.kawa.net/xp/images/xp-title-128x32.gif",
"title":"Kawa.net xp - Ajax&Perl技術情報(川崎有亮)"},
"item":[
{"dc:date":"2011-01-12T02:55:00+09:00",
"description":"openFrameworks では、Windows・Mac 向けアプリに限らず、ofxiPhone アドオンでiPhone・iPod touch・iPad 上で稼働するアプリケーションを開発することができます。OpenCV など openFrameworks のライブラリを手軽に iPhone 上で使えるらしい。",
"dc:creator":"Kawanet Tech Blog",
"title":"openFrameworks+ofxiPhoneでiPhone・iPad用Hello, world!",
"link":"http://kawa.at.webry.info/201101/article_3.html",
"rdf:about":"http://kawa.at.webry.info/201101/article_3.html",
"dc:subject":["Xcode","openFrameworks","iPhone"]},
................................以下略
<?xml version='1.0' encoding='UTF-8'?>
<abc version='0.1'>
auuauauuaau
byaaaaaaaaaaaaaaaou
this is test text.
<version><moe>gyoeee uhehe</moe></version>
<list>
uiuiueoooo
<file permisssion='0777' size='10485760'><![CDATA[/var/test.dat]]></file>
<file><![CDATA[/var/test0.dat]]></file>
<file permisssion='0644' modifiedTime='1296081769'><![CDATA[/var/test1.dat]]></file>
<file></file>
<file permisssion='0744' owner='0' group='0' checksum='0'><![CDATA[/var/test2.dat]]></file>
myaoooou
<file><![CDATA[%2f%2f%2f"\\""%2f%2f%2f]]></file>
</list>
hieeeeeeeeeeeee
</abc>
{"abc":
{"#text":["auuauauuaau\nbyaaaaaaaaaaaaaaaou\nthis is test text.","hieeeeeeeeeeeee"],
"version":["0.1",
{"moe":"gyoeee uhehe"}],
"list":
{"#text":["uiuiueoooo","myaoooou"],
"file":[
{"permisssion":"0777",
"#text":"/var/test.dat",
"size":"10485760"},"/var/test0.dat",
{"permisssion":"0644",
"#text":"/var/test1.dat",
"modifiedTime":"1296081769"},
{"owner":"0",
"permisssion":"0744",
"#text":"/var/test2.dat",
"group":"0",
"checksum":"0"},"%2f%2f%2f\"\\"\"%2f%2f%2f"]}}}
- 連続したテキストは\nで結合する。
- attributeとnodeに同じものがあったら結合する。
- 空ノードは捨てる。
- CDATAは普通のテキストにする。