Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

root/trunk/dejavu/json.py

Revision 556 (checked in by fumanchu, 1 year ago)

Bug in json.dict_to_unit (wasn't fully-populating the unit._properties dict).

  • Property svn:eol-style set to native
Line 
1 """JSON conversion support for Dejavu Units."""
2
3 import datetime
4 import decimal
5 import time
6
7 import dejavu
8 from simplejson import JSONEncoder, JSONDecoder
9
10 __all__ = ["Encoder", "Decoder", "Converter"]
11
12
13 class Encoder(JSONEncoder):
14     """Extends the base simplejson JSONEncoder for date and decimal types."""
15    
16     def push_date(self, d):
17         """Serialize the given datetime.date object to a JSON string."""
18         # Default is ISO 8601 compatible (standard notation).
19         return "%04d-%02d-%02d" % (d.year, d.month, d.day)
20    
21     def push_time(self, t):
22         """Serialize the given datetime.time object to a JSON string."""
23         # Default is ISO 8601 compatible (standard notation).
24         return "%02d:%02d:%02d" % (t.hour, t.minute, t.second)
25    
26     def push_datetime(self, dt):
27         """Serialize the given datetime.datetime object to a JSON string."""
28         # Default is ISO 8601 compatible (standard notation).
29         # Don't use strftime because that can't handle dates before 1900.
30         return ("%04d-%02d-%02d %02d:%02d:%02d" %
31                 (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second))
32    
33     def default(self, o):
34         # We MUST check for a datetime.datetime instance before datetime.date.
35         # datetime.datetime is a subclass of datetime.date, and therefore
36         # instances of it are also instances of datetime.date.
37         if isinstance(o, datetime.datetime):
38             return {'__datetime__': True, 'value': self.push_datetime(o)}
39         elif isinstance(o, datetime.date):
40             return {'__date__': True, 'value': self.push_date(o)}
41         elif isinstance(o, datetime.time):
42             return {'__time__': True, 'value': self.push_time(o)}
43         elif isinstance(o, decimal.Decimal):
44             return {'__decimal__': True, 'value': unicode(o)}
45         else:
46             return JSONEncoder.default(self, o)
47
48
49 class Decoder(JSONDecoder):
50     """Extends the base simplejson JSONDecoder for Dejavu."""
51    
52     def __init__(self, encoding=None, object_hook=None):
53         if object_hook is None:
54             object_hook = self.json_to_python
55         JSONDecoder.__init__(self, encoding, object_hook)
56    
57     def pull_date(self, value):
58         """Return a datetime.date object from the given JSON string."""
59         chunks = (value[0:4], value[5:7], value[8:10])
60         return datetime.date(*map(int, chunks))
61    
62     def pull_time(self, value):
63         """Return a datetime.time object from the given JSON string."""
64         chunks = (value[0:2], value[3:5], value[6:8])
65         return datetime.time(*map(int, chunks))
66    
67     def pull_datetime(self, value):
68         """Return a datetime.datetime object from the given JSON string."""
69         chunks = (value[0:4], value[5:7], value[8:10],
70                   value[11:13], value[14:16], value[17:19],
71                   value[20:26] or 0)
72         return datetime.datetime(*map(int, chunks))
73    
74     def json_to_python(self, d):
75         if '__datetime__' in d:
76             return self.pull_datetime(d['value'])
77         if '__date__' in d:
78             return self.pull_date(d['value'])
79         if '__time__' in d:
80             return self.pull_time(d['value'])
81         if '__decimal__' in d:
82             return decimal.Decimal(d['value'])
83         return d
84
85
86 class Converter(object):
87     """Provides two-way conversion of Units/JSON via loads and dumps methods.
88     
89     Also converts datetime.date, datetime.time, datetime.datetime and
90     decimal.Decimal to/from JSON.
91
92     This is accomplished by the Encoder and Decoder classes, which are
93     subclasses of their counterparts in simplejson.  If you wish to change
94     the output of the converter at all, you should probably subclass the
95     Encoder/Decoder and then make a cusom Converter subclass with your
96     encoder/decoder as class attributes.
97     """
98    
99     encoder = Encoder
100     decoder = Decoder
101    
102     def loads(self, s, encoding=None, **kw):
103         return self.decoder(encoding=encoding, **kw).decode(s)
104
105     def dumps(self, obj, skipkeys=False, ensure_ascii=False,
106               check_circular=True, allow_nan=True, indent=None, **kw):
107         return self.encoder(skipkeys, ensure_ascii, check_circular,
108                             allow_nan, indent, **kw).encode(obj)
109
110
111 def unit_to_dict(unit):
112     """Return a JSON-able dict for the given Dejavu Unit instance."""
113     d = unit._properties.copy()
114     d['__dejavu.class__'] = unit.__class__.__name__
115     return d
116
117
118 def dict_to_unit(d, store=None, cls=None):
119     """Return a Unit instance from a dict of UnitProperty values."""
120     if cls is None:
121         clsname = d['__dejavu.class__']
122         cls = store.class_by_name(clsname)
123    
124     unit = cls()
125     # Set _properties directly to avoid __set__ overhead.
126     # As always, this must be FULLY populated, even if some values are None.
127     params = {}
128     for k in cls.properties:
129         v = d.get(k, None)
130         # The JSON serializer stores tuples as lists
131         if v is not None and getattr(cls, k).type is tuple:
132             v = tuple(v)
133         params[k] = v
134     unit._properties = params
135     unit.cleanse()
136     return unit
Note: See TracBrowser for help on using the browser.