2012-01-09 36 views
21

Tôi có một chuỗi đại diện của một đối tượng JSON.Làm thế nào để chuyển đổi sang một đối tượng datetime Python với JSON.loads?

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}' 

Khi tôi gọi json.loads với đối tượng này;

json.loads(dumped_dict) 

Tôi nhận được;

{'created_at': '2020-08-09T11:24:20', 'debug': False} 

Không có gì sai ở đây. Tuy nhiên, tôi muốn biết nếu có một cách để chuyển đổi các đối tượng trên với json.loads một cái gì đó như thế này:

{'created_at': datetime.datetime(2020, 08, 09, 11, 24, 20), 'debug': False} 

thời gian ngắn, chúng ta có thể chuyển đổi chuỗi datetime đối tượng datetime.datetime thực tế trong khi gọi json.loads?

Trả lời

17

Giải pháp của tôi cho đến nay:

>>> json_string = '{"last_updated": {"$gte": "Thu, 1 Mar 2012 10:00:49 UTC"}}' 
>>> dct = json.loads(json_string, object_hook=datetime_parser) 
>>> dct 
{u'last_updated': {u'$gte': datetime.datetime(2012, 3, 1, 10, 0, 49)}} 


def datetime_parser(dct): 
    for k, v in dct.items(): 
     if isinstance(v, basestring) and re.search("\ UTC", v): 
      try: 
       dct[k] = datetime.datetime.strptime(v, DATE_FORMAT) 
      except: 
       pass 
    return dct 

Để tham khảo thêm về việc sử dụng object_hook: JSON encoder and decoder

Trong trường hợp của tôi chuỗi json đến từ một yêu cầu GET để REST API của tôi. Giải pháp này cho phép tôi 'get đúng ngày' minh bạch, mà không ép buộc khách hàng và người sử dụng vào các tiền tố hardcoding như __date__ vào JSON, miễn là chuỗi đầu vào phù hợp với DATE_FORMAT đó là:

DATE_FORMAT = '%a, %d %b %Y %H:%M:%S UTC' 

Các mô hình regex nên có thể được tinh chỉnh thêm

PS: trong trường hợp bạn đang phân vân, json_string là truy vấn MongoDB/PyMongo.

+0

Vui lòng cung cấp một số phản hồi/đề xuất khác với -1 đơn giản, vì vậy tôi có thể tìm hiểu điều gì đó ít nhất :) –

+0

Tuyệt đối đã cứu tôi. – David

+0

@NicolaIarocci trông giống như một giải pháp tuyệt vời, tuy nhiên không phải điều này cũng buộc khách hàng phải hardcode một hậu tố "UTC" vào json của họ? –

1

Theo như tôi biết không có giải pháp hộp nào cho việc này.

Trước hết, giải pháp cần tính đến json schema để phân biệt chính xác giữa các chuỗi và thời gian biểu. Ở một mức độ nào đó, bạn có thể đoán lược đồ với trình inferencer lược đồ json (google for json schema inferencer github) và sau đó sửa chữa các địa điểm thực sự là datetimes.

Nếu lược đồ được biết, sẽ khá dễ dàng để tạo một hàm, phân tích cú pháp json và thay thế các biểu diễn chuỗi bằng datetime. Một số nguồn cảm hứng cho mã có lẽ có thể được tìm thấy từ validictory sản phẩm (và xác nhận lược đồ json cũng có thể là ý tưởng tốt).

3

Cách đặt câu hỏi của bạn, không có dấu hiệu nào cho biết rằng chuỗi là giá trị ngày tháng. Đây là khác biệt so với các tài liệu của json trong đó có chuỗi Ví dụ:

'{"__complex__": true, "real": 1, "imag": 2}' 

Chuỗi này có một chỉ báo "__complex__": true có thể được sử dụng để suy ra các loại dữ liệu, nhưng trừ khi có một chỉ số như vậy, một chuỗi là chỉ là một chuỗi, và tất cả những gì bạn có thể làm là chuẩn bị lại theo cách của bạn thông qua tất cả các chuỗi và quyết định xem chúng có giống như ngày tháng hay không.

Trong trường hợp của bạn, bạn chắc chắn nên sử dụng lược đồ nếu có sẵn lược đồ cho định dạng của bạn.

+0

Tài liệu chính xác của json đề xuất sử dụng tên được nhấn kép là gì? Tôi đã thấy \ _ \ _ loại, ví dụ, nhưng tất cả những người giống như các công ước với việc sử dụng hạn chế. –

+0

Ví dụ được lấy từ tài liệu gói 'json'. –

14

Bạn cần phải vượt qua một đối tượng object_hook.Từ documentation:

object_hook là một chức năng tùy chọn sẽ được gọi với kết quả của bất kỳ đối tượng theo nghĩa đen được giải mã (một dict). Giá trị trả lại của object_hook sẽ được sử dụng thay cho dict.

Như thế này:

import datetime 
import json 

def date_hook(json_dict): 
    for (key, value) in json_dict.items(): 
     try: 
      json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S") 
     except: 
      pass 
    return json_dict 

dumped_dict = '{"debug": false, "created_at": "2020-08-09T11:24:20"}' 
loaded_dict = json.loads(dumped_dict, object_hook=date_hook) 

Nếu bạn cũng muốn xử lý các múi giờ bạn sẽ phải sử dụng dateutil thay vì strptime.

+1

Sử dụng try/catch như một cấu trúc điều khiển không phải là lý tưởng. – Maciej

1

Bạn có thể sử dụng regex để xác định có hay không bạn muốn chuyển đổi một lĩnh vực nhất định để datetime như vậy:

def date_hook(json_dict): 
    for (key, value) in json_dict.items(): 
     if type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d*$', value): 
      json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") 
     elif type(value) is str and re.match('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$', value): 
      json_dict[key] = datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S") 
     else: 
      pass 

    return json_dict 

Sau đó, bạn có thể tham khảo các chức năng date_hook sử dụng tham số object_hook trong lệnh gọi json.loads():

json_data = '{"token": "faUIO/389KLDLA", "created_at": "2016-09-15T09:54:20.564"}' 
data_dictionary = json.loads(json_data, object_hook=date_hook) 
3

tôi sẽ làm tương tự như Nicola đề nghị với 2 thay đổi:

  1. Sử dụng dateutil.parser thay vì datetime.datetime.strptime
  2. Xác định rõ ràng ngoại lệ nào tôi muốn nắm bắt. Tôi thường khuyên bạn nên tránh bằng mọi giá có một sản phẩm nào except:

Hoặc trong mã:

import dateutil.parser 

def datetime_parser(json_dict): 
    for (key, value) in json_dict.items(): 
     try: 
      json_dict[key] = dateutil.parser.parse(value) 
     except (ValueError, AttributeError): 
      pass 
    return json_dict 

str = "{...}" # Some JSON with date 
obj = json.loads(str, object_hook=datetime_parser) 
print(obj) 
+0

Hướng thích thú để thử. Nhưng có vẻ hơi chậm để chạy một phân tích datetime trên mọi mục trong json. Hầu hết các mục sẽ không phải là giá trị datetime. – swdev

0

Lấy cảm hứng từ Nicola của answer và thích nghi với python3 (str thay vì basestring):

import re 
from datetime import datetime 
datetime_format = "%Y-%m-%dT%H:%M:%S" 
datetime_format_regex = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$') 


def datetime_parser(dct): 
    for k, v in dct.items(): 
     if isinstance(v, str) and datetime_format_regex.match(v): 
      dct[k] = datetime.strptime(v, datetime_format) 
    return dct 

Điều này tránh sử dụng cơ chế thử/ngoại trừ. Mở mã kiểm tra OP của:

>>> import json 
>>> json_string = '{"debug": false, "created_at": "2020-08-09T11:24:20"}' 
>>> json.loads(json_string, object_hook=datetime_parser) 
{'created_at': datetime.datetime(2020, 8, 9, 11, 24, 20), 'debug': False} 

Các regex và datetime_format biến có thể dễ dàng thích nghi để phù hợp với mô hình khác, ví dụ không có T ở giữa.

Để chuyển đổi chuỗi được lưu trong isoformat (do đó được lưu trữ bằng micro giây) trở lại đối tượng ngày giờ, hãy tham khảo this question.

Các vấn đề liên quan