python json 序列化及反序列化

python json 序列化及反序列化

使用namedtuple

反序列化为 namedtuple

1
2
3
4
5
6
7
8
import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

序列化为 json

1
json.dumps(x._asdict())

输出

1
{"hometown": ["New York", 123], "name": "John Smith"}

封装:

1
2
3
4
def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

总结:

序列化及反序列化都比较方便,但是 namedtuple 不能进行复制,不能修改

使用object_hook

反序列化为对象

1
2
3
4
5
6
7
8
9
10
11
class JSONObject:
def __init__(self, d):
self.__dict__ = d

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

a = json.loads(data,
object_hook=JSONObject)

a.name = "changed"
print a.name

获取对象属性

  • 使用 getattr
1
2
3
4
print getattr(a.hometown, 'id', 321)
# 123
print getattr(a.hometown, 'id1', 321)
# 321
  • 使用 try
1
2
3
4
try:
print a.hometown.id2
except AttributeError as ex:
print ex
  • 使用 get
1
x = data.get('first', {}).get('second', {}).get('third', None)

获取对象的嵌套属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def multi_getattr(obj, attr, default = None):
"""
Get a named attribute from an object; multi_getattr(x, 'a.b.c.d') is
equivalent to x.a.b.c.d. When a default argument is given, it is
returned when any attribute in the chain doesn't exist; without
it, an exception is raised when a missing attribute is encountered.

"""
attributes = attr.split(".")
for i in attributes:
try:
obj = getattr(obj, i)
except AttributeError:
if default:
return default
else:
raise
return obj

print multi_getattr(a, "hometown.name")
# New York
print multi_getattr(a, "hometown.name1", "abc")
# abc
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# coding=utf-8
from __future__ import unicode_literals
import collections
import operator

_default_stub = object()


def deep_get(obj, path, default=_default_stub, separator='.'):
"""Gets arbitrarily nested attribute or item value.

Args:
obj: Object to search in.
path (str, hashable, iterable of hashables): Arbitrarily nested path in obj hierarchy.
default: Default value. When provided it is returned if the path doesn't exist.
Otherwise the call raises a LookupError.
separator: String to split path by.

Returns:
Value at path.

Raises:
LookupError: If object at path doesn't exist.

Examples:
>>> deep_get({'a': 1}, 'a')
1

>>> deep_get({'a': 1}, 'b')
Traceback (most recent call last):
...
LookupError: {u'a': 1} has no element at 'b'

>>> deep_get(['a', 'b', 'c'], -1)
u'c'

>>> deep_get({'a': [{'b': [1, 2, 3]}, 'some string']}, 'a.0.b')
[1, 2, 3]

>>> class A(object):
... def __init__(self):
... self.x = self
... self.y = {'a': 10}
...
>>> deep_get(A(), 'x.x.x.x.x.x.y.a')
10

>>> deep_get({'a.b': {'c': 1}}, 'a.b.c')
Traceback (most recent call last):
...
LookupError: {u'a.b': {u'c': 1}} has no element at 'a'

>>> deep_get({'a.b': {'Привет': 1}}, ['a.b', 'Привет'])
1

>>> deep_get({'a.b': {'Привет': 1}}, 'a.b/Привет', separator='/')
1

"""
if isinstance(path, basestring):
attributes = path.split(separator)
elif isinstance(path, collections.Iterable):
attributes = path
else:
attributes = [path]

LOOKUPS = [getattr, operator.getitem, lambda obj, i: obj[int(i)]]
try:
for i in attributes:
for lookup in LOOKUPS:
try:
obj = lookup(obj, i)
break
except (TypeError, AttributeError, IndexError, KeyError,
UnicodeEncodeError, ValueError):
pass
else:
msg = "{obj} has no element at '{i}'".format(obj=obj, i=i)
raise LookupError(msg.encode('utf8'))
except Exception:
if _default_stub != default:
return default
raise
return obj

Sync From: https://github.com/TheBigFish/blog/issues/2