-
Notifications
You must be signed in to change notification settings - Fork 4
/
json_lxml.py
67 lines (52 loc) · 1.6 KB
/
json_lxml.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# -*- coding: utf-8 -*-
"""convert between python value and lxml.etree so that lxml's power can be applied to json data.
In particular, xpath queries can be run against json.
"""
from lxml import etree
type_map = dict(
int=int,
bytes=bytes,
NoneType=lambda x: None,
list=list,
bool=bool,
str=str,
)
NoneType = type(None)
def element(k, v):
""" key, val --> etree.Element(key)
"""
node = etree.Element(k)
if isinstance(v, dict):
for ck, cv in v.items():
node.append(element(ck, cv))
elif isinstance(v, unicode):
node.set('type', type(v).__name__)
node.text=v.encode('utf8')
elif isinstance(v, (int, float, bool, str, NoneType)): # scalar
node.set('type', type(v).__name__)
node.text = str(v)
elif isinstance(v, list):
node.set('type', type(v).__name__) # list xx this could be done across the board.
for i, cv in enumerate(v):
node.append(element("_list_element_%d" % i, cv))
else:
assert False
return node
def value(e):
""" etree.Element --> value
"""
children = e.getchildren()
typ = e.get('type')
if children:
if not typ: # xx defaults dict
return dict((c.tag, value(c)) for c in children)
elif typ == 'list':
return [value(c) for c in children]
else:
raise TypeError('unexpected type', typ)
# convert it back to the right python type
ctor = type_map[typ]
return ctor(e.text)
def xpath(val, xp):
for node in element('root', val).xpath(xp):
yield value(node)