2012-08-25 10 views
11

có cách nào thanh lịch để hỗ trợ datetime mã hóa JSON JSON không? một số mô-đun bên thứ 3 hoặc dễ hack?Bộ mã hóa JSON Python để hỗ trợ datetime?

Tôi đang sử dụng trình bao bọc cơ sở dữ liệu của cơn lốc xoáy để tìm nạp một số raw từ db để tạo ra một json. Kết quả truy vấn bao gồm cột dấu thời gian MySQL thông thường.

Thật khó chịu khi bộ mã hóa json mặc định của Python không hỗ trợ kiểu datetime của nó, điều này rất phổ biến trong tất cả các loại truy vấn cơ sở dữ liệu.

Tôi không muốn sửa đổi bộ mã hóa json của riêng Python. bất kỳ thực hành tốt nào? Cảm ơn rất nhiều!

ps: Tôi tìm thấy một bẩn hack bằng cách sửa đổi Python JSON phương pháp mã hóa mặc định:

Thay đổi:

def default(self, o): 
    raise TypeError(repr(o) + " is not JSON serializable") 

Để:

def default(self, o): 
    from datetime import date 
    from datetime import datetime 
    if isinstance(o, datetime): 
     return o.isoformat() 
    elif isinstance(o, date): 
     return o.isoformat() 
    else: 
     raise TypeError(repr(o) + " is not JSON serializable") 

tốt, nó sẽ là một giải pháp tạm thời chỉ dành cho môi trường dev.

Nhưng đối với môi trường sản xuất hoặc giải pháp lâu dài, điều này khá xấu xí và tôi phải thực hiện sửa đổi mỗi khi triển khai lên máy chủ mới.

Có cách nào tốt hơn không? Tôi không muốn sửa đổi mã Python, không phải mã nguồn Tornado. Có điều gì tôi có thể làm với mã dự án của riêng tôi để thực hiện điều này không? tốt nhất là ở một tốc độ.

Cảm ơn rất nhiều!

+1

Xem: http://stackoverflow.com/questions/455580/json-datetime-between-python-and-javascript –

+0

vấn đề với phương pháp phân lớp, là nó không cho tất cả các sử dụng khác của mã hóa json, chẳng hạn như django đơn giản, "dumpdata" – mcr

Trả lời

17

The docs suggest subclassing JSONEncoder và thực hiện các phương pháp mặc định của riêng bạn. Có vẻ như bạn về cơ bản ở đó, và nó không phải là một "hack bẩn".

Ngày lý do không được xử lý bởi bộ mã hóa mặc định là không có biểu diễn chuẩn của một ngày trong JSON. Some people đang sử dụng định dạng /Date(1198908717056)/, nhưng tôi thích định dạng ISO cá nhân.

import datetime 

class DateTimeEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if isinstance(obj, datetime.datetime): 
      return obj.isoformat() 
     elif isinstance(obj, datetime.date): 
      return obj.isoformat() 
     elif isinstance(obj, datetime.timedelta): 
      return (datetime.datetime.min + obj).time().isoformat() 
     else: 
      return super(DateTimeEncoder, self).default(obj) 

DateTimeEncoder().encode(object) 
+0

cảm ơn rất nhiều. this isoformat() làm cho nó trông đẹp hơn. :) – horacex

0

Chuyển đổi loại ngày giờ thành dấu thời gian unix, sau đó mã hóa nội dung thành một json.

ví dụ: : http://codepad.org/k3qF09Kr

+0

bạn có nghĩa là thay đổi loại datetime mặc định của Python? làm thế nào để làm như vậy? nó sẽ là quá nguy hiểm? nó sẽ phá vỡ mọi thứ? – horacex

+0

@horacex không, chỉ cần sửa đổi resultset đến từ trình bao bọc cơ sở dữ liệu cơn lốc xoáy. – DhruvPathak

7

tôi đã thực hiện các lớp học của riêng mình cho dự án của tôi:

import datetime 
import decimal 
import json 
import sys 

class EnhancedJSONEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if isinstance(obj, datetime.datetime): 
      ARGS = ('year', 'month', 'day', 'hour', 'minute', 
        'second', 'microsecond') 
      return {'__type__': 'datetime.datetime', 
        'args': [getattr(obj, a) for a in ARGS]} 
     elif isinstance(obj, datetime.date): 
      ARGS = ('year', 'month', 'day') 
      return {'__type__': 'datetime.date', 
        'args': [getattr(obj, a) for a in ARGS]} 
     elif isinstance(obj, datetime.time): 
      ARGS = ('hour', 'minute', 'second', 'microsecond') 
      return {'__type__': 'datetime.time', 
        'args': [getattr(obj, a) for a in ARGS]} 
     elif isinstance(obj, datetime.timedelta): 
      ARGS = ('days', 'seconds', 'microseconds') 
      return {'__type__': 'datetime.timedelta', 
        'args': [getattr(obj, a) for a in ARGS]} 
     elif isinstance(obj, decimal.Decimal): 
      return {'__type__': 'decimal.Decimal', 
        'args': [str(obj),]} 
     else: 
      return super().default(obj) 


class EnhancedJSONDecoder(json.JSONDecoder): 

    def __init__(self, *args, **kwargs): 
     super().__init__(*args, object_hook=self.object_hook, 
         **kwargs) 

    def object_hook(self, d): 
     if '__type__' not in d: 
      return d 
     o = sys.modules[__name__] 
     for e in d['__type__'].split('.'): 
      o = getattr(o, e) 
     args, kwargs = d.get('args',()), d.get('kwargs', {}) 
     return o(*args, **kwargs) 

if __name__ == '__main__': 
    j1 = json.dumps({'now': datetime.datetime.now(), 
     'val': decimal.Decimal('9.3456789098765434987654567')}, 
     cls=EnhancedJSONEncoder) 
    print(j1) 
    o1 = json.loads(j1, cls=EnhancedJSONDecoder) 
    print(o1) 

Kết quả:

{"val": {"args": ["9.3456789098765434987654567"], "__type__": "decimal.Decimal"}, "now": {"args": [2014, 4, 29, 11, 44, 57, 971600], "__type__": "datetime.datetime"}} 
{'val': Decimal('9.3456789098765434987654567'), 'now': datetime.datetime(2014, 4, 29, 11, 44, 57, 971600)} 

Tài liệu tham khảo:

Lưu ý: Nó có thể được thực hiện linh hoạt hơn bằng cách đi qua một từ điển tùy chỉnh với các loại như phím và args, kwargs như giá trị cho bộ mã hóa của __init__() và sử dụng đó (hoặc một từ điển mặc định) trong default().

3

json.dumps(thing, default=str)

+0

xin vui lòng upvote nếu bạn nghĩ rằng điều này đáp ứng các định nghĩa của "hack dễ dàng" được đề cập trong câu hỏi. –

0

Chỉ cần tạo một bộ mã hóa tùy chỉnh

(việc bổ sung nhỏ nhưng quan trọng đối với câu trả lời của Cole là việc xử lý pd.NaT (hoặc/giá trị timestamp trống null), vì không có thêm bạn sẽ nhận được chuyển đổi dấu thời gian rất lạ cho NAT/dữ liệu timestamp thiếu)

class CustomEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if pd.isnull(obj): 
      return None 
     elif isinstance(obj, datetime): 
      return obj.isoformat() 
     elif isinstance(obj, date): 
      return obj.isoformat() 
     elif isinstance(obj, timedelta): 
      return (datetime.min + obj).time().isoformat() 
     else: 
      return super(CustomEncoder, self).default(obj) 

Sau đó sử dụng nó để mã hóa một dataframe:

df_as_dict = df.to_dict(outtype = 'records') # transform to dict 

df_as_json = CustomEncoder().encode(df_as_dict) #transform to json 

Kể từ khi mã hóa tiêu chuẩn hóa dữ liệu, các bộ giải mã thường xuyên sẽ đóng vai trò tốt trong chuyển nó trở lại một dataframe:

result_as_dict = json.JSONDecoder().decode(df_as_json) # decode back to dict 

result_df = pd.DataFrame(result) # transform dict back to dataframe 

Tất nhiên điều này cũng sẽ làm việc nếu bạn đặt dataframe thành một dict lớn hơn trước khi mã hóa, ví dụ

input_dict = {'key_1':val_1,'key_2':val_2,...,'df_as_dict':df_as_dict} 
input_json = CustomEncoder().encode(input_dict) 
input_json_back_as_dict = json.JSONDecoder().decode(input_json) 
input_df_back_as_dict = input_json_back_as_dict['df_as_dict'] 
input_df_back_as_df = pd.DataFrame(input_df_back_as_dict) 
1
json.dumps(r, default=lambda o: o.isoformat() if hasattr(o, 'isoformat') else o) 
+1

Mặc dù đoạn mã này có thể giải quyết được câu hỏi, [bao gồm cả giải thích] (http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. – andreas