JMESPath là một ngôn ngữ truy vấn JSON. JMESPath trong Python cho phép bạn lấy dữ liệu bạn cần từ tài liệu JSON hoặc từ điển một cách dễ dàng. Thư viện này có sẵn cho Python, nhưng cũng có sẵn cho nhiều ngôn ngữ lập trình khác, nghĩa là nếu bạn thành thạo ngôn ngữ truy vấn JMESPath, bạn có thể sử dụng nó ở nhiều nơi
Mục lục
Vấn đề JMESPath giải quyết
Như chúng ta đã thấy ở trang trước, thật dễ dàng để lấy giá trị lồng nhau từ từ điển Python bằng thư viện JSON của chính Python. Ví dụ.
>>> jmespath.search['persons[0]', persons] {'name': 'erik', 'age': 38} >>> jmespath.search['persons[0].age', persons] 381 sẽ cung cấp cho bạn giá trị lồng nhau của tuổi trong một tài liệu giống như thế này
{ "persons": { "name": "erik", "age": "38" } }
Nhưng nếu bạn muốn trích xuất tất cả các trường tuổi từ một mảng người, trong một tài liệu như bên dưới thì sao?
{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }
Chúng ta có thể viết một vòng lặp for Python và lặp qua tất cả những người. Dễ như ăn bánh. Nhưng các vòng lặp chậm và gây phức tạp cho mã của bạn. Đây là nơi JMESPath phát huy tác dụng
Cài đặt JMESPath cho Python
JMESPath không phải là một phần của thư viện chuẩn Python, nghĩa là bạn sẽ cần cài đặt nó với pip hoặc pipenv. Gói JMESPath được lưu trữ trên PyPI, giống như hầu hết các gói Python. Nó có sẵn dưới cái tên không gây ngạc nhiên jmespath
Ví dụ: khi sử dụng pip trong môi trường ảo, bạn có thể cài đặt bằng pip và nhập mô-đun như sau
$ pip3 install jmespath $ python3 Python 3.8.2 [default, Jul 16 2020, 14:00:26] >>> import jmespath >>> j = { "people": [{ "name": "erik", "age": 38 }] } >>> jmespath.search["people[*].age", j] [38] >>>
Các ví dụ về JMESPath Python
Hãy bắt đầu với một số trường hợp sử dụng đơn giản. Chúng tôi sẽ lấy người đầu tiên từ mảng, sau đó lấy tuổi của người đầu tiên
>>> jmespath.search['persons[0]', persons] {'name': 'erik', 'age': 38} >>> jmespath.search['persons[0].age', persons] 38
Trong báo cáo vấn đề ở trên, chúng tôi muốn trích xuất tất cả các trường tuổi từ mảng người trong tài liệu JSON. Biểu thức JMESPath này sẽ hoàn thành công việc
>>> import jmespath >>> persons = { .. "persons": [ .. { "name": "erik", "age": 38 }, .. { "name": "john", "age": 45 }, .. { "name": "rob", "age": 14 } .. ] .. } >>> jmespath.search['persons[*].age', persons] [38, 45, 14]
Giả sử bạn muốn lọc danh sách và chỉ lấy độ tuổi của những người có tên 'erik'. Bạn có thể làm như vậy với một bộ lọc
>>> jmespath.search["persons[?name=='erik'].age", persons] [38]
Lưu ý rằng chúng tôi hiện đang sử dụng dấu ngoặc kép vì chúng tôi cần trích dẫn tên bên trong biểu thức bộ lọc
Tất cả chúng ta đều là những người làm dữ liệu ở đây, vì vậy bạn đã biết kịch bản. Nó xảy ra 1 đến 100 lần mỗi ngày. Bạn bắt đầu làm việc với API REST mới và nó hoàn toàn phù hợp với nhu cầu của bạn. Thật dễ dàng để chuyển yêu cầu và phản hồi chứa tất cả thông tin bạn đang tìm kiếm
Tuy nhiên, chỉ có một vấn đề. sự phức tạp của các đối tượng JSON lồng nhau là vô tận, và đột nhiên công việc bạn yêu thích cần phải tạm dừng để truy xuất dữ liệu bạn muốn một cách tỉ mỉ, và đó là 5 cấp độ sâu trong địa ngục JSON lồng nhau. Không ai cảm thấy mình giống một "nhà khoa học" hay một "kỹ sư" khi nửa ngày của họ xử lý các lỗi giá trị quan trọng
May mắn thay, chúng tôi viết mã bằng Python. [được rồi, ngôn ngữ không tạo ra nhiều sự khác biệt ở đây. Nó giống như một cuộc gọi tập hợp vào thời điểm đó]
Sử dụng API Google Maps làm ví dụ
Để hình dung vấn đề, hãy lấy một ví dụ mà ai đó có thể muốn sử dụng. API Google Maps là một ứng cử viên sáng giá để phù hợp với hóa đơn tại đây
Mặc dù Google Maps là một tập hợp các API, nhưng Ma trận khoảng cách của Google Maps. Ý tưởng là với một lệnh gọi API duy nhất, người dùng có thể tính toán khoảng cách và thời gian di chuyển giữa điểm gốc và vô số điểm đến. Đó là một API đầy đủ tính năng tuyệt vời, nhưng như bạn có thể tưởng tượng, JSON kết quả để tính toán thời gian đi lại giữa nơi bạn đứng và mọi vị trí trong vũ trụ có thể hình dung được tạo nên một cấu trúc JSON cực kỳ phức tạp
Cảm nhận địa ngục JSON
Chúng tôi sẽ sử dụng API Google Maps để biết khoảng cách giữa hai địa điểm, cũng như thời gian ước tính để hoàn thành chuyến đi. Dưới đây chúng ta thấy một yêu cầu như vậy sẽ được thực hiện như thế nào thông qua thư viện yêu cầu của Python
Chúng ta cần truyền một số tham số
- nguồn gốc. Địa điểm [hoặc địa điểm] đại diện cho nơi chuyến đi của chúng tôi bắt đầu. Giá trị này có thể được chuyển dưới dạng tên thành phố, địa chỉ hoặc các định dạng khác;
- điểm đến. Tương đương với tham số
3 cho [các] điểm đến của chuyến đi{ "destination_addresses": [ "Philadelphia, PA, USA" ], "origin_addresses": [ "New York, NY, USA" ], "rows": [{ "elements": [{ "distance": { "text": "94.6 mi", "value": 152193 }, "duration": { "text": "1 hour 44 mins", "value": 6227 }, "status": "OK" }] }], "status": "OK" }
- Chìa khóa. Khóa API của Google
- transit_mode [Tùy chọn]. Phương thức vận chuyển cho chuyến đi [thông báo thời gian chuyến đi]
- đơn vị [Tùy chọn]. Có trả về giá trị dưới dạng đơn vị
4 hoặc{ "destination_addresses": [ "Philadelphia, PA, USA" ], "origin_addresses": [ "New York, NY, USA" ], "rows": [{ "elements": [{ "distance": { "text": "94.6 mi", "value": 152193 }, "duration": { "text": "1 hour 44 mins", "value": 6227 }, "status": "OK" }] }], "status": "OK" }
5 hay không{ "destination_addresses": [ "Philadelphia, PA, USA" ], "origin_addresses": [ "New York, NY, USA" ], "rows": [{ "elements": [{ "distance": { "text": "94.6 mi", "value": 152193 }, "duration": { "text": "1 hour 44 mins", "value": 6227 }, "status": "OK" }] }], "status": "OK" }
"""Fetch and extract JSON data from Google Maps."""
import requests
from config import API_KEY
def google_maps_distance[]:
"""Fetch distance between two points."""
endpoint = "//maps.googleapis.com/maps/api/distancematrix/json"
params = {
'units': 'imperial',
'key': API_KEY,
'origins': 'New York City, NY',
'destinations': 'Philadelphia,PA',
'transit_mode': 'car'
}
resp = requests.get[endpoint, params=params]
return resp.json[]
Đưa ra một yêu cầu đơn giản tới Google Maps APIVì chúng tôi đang xử lý một nguồn gốc và một đích duy nhất, phản hồi JSON kết quả cho yêu cầu này tương đối đơn giản
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
Đầu ra của {
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
6Đối với mỗi điểm đến, chúng tôi nhận được hai điểm dữ liệu. khoảng cách đi lại và thời lượng ước tính. Nếu theo giả thuyết chúng tôi muốn trích xuất các giá trị đó, thì việc nhập
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
7 không quá điên rồ. Ý tôi là, nó hơi khủng khiếp và khiến người ta thường có ý nghĩ tự tử, nhưng không có gì khác thường cả.Bây giờ, hãy làm cho mọi thứ trở nên thú vị bằng cách thêm một vài điểm dừng nữa trong chuyến đi của chúng ta
{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }3Đưa ra một yêu cầu phức tạp đối với Google Maps API
Ôi mẹ kiếp
{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }4Đầu ra của
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
6Rất nhiều điều đang xảy ra ở đây. Có những đối tượng. có danh sách. Có danh sách các đối tượng là một phần của đối tượng. Điều cuối cùng tôi muốn giải quyết là cố gắng phân tích dữ liệu này chỉ để vô tình nhận được một cặp khóa/giá trị vô dụng như "trạng thái". "VÂNG. "
Đoạn mã để giải cứu
Giả sử chúng ta chỉ muốn dữ liệu mà con người có thể đọc được từ JSON này, được gắn nhãn "văn bản" cho cả khoảng cách và thời lượng. Chúng tôi đã tạo một chức năng bên dưới có tên là
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
9 để giúp giải quyết vấn đề này. Chức năng này được dự định là linh hoạt và bất khả tri, do đó có thể được nhập dưới dạng mô-đun vào bất kỳ dự án nào bạn có thể cần{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }7trích xuất. py
Chúng ta cần truyền cho hàm này hai giá trị
- Một từ điển Python phức tạp, chẳng hạn như phản hồi mà chúng tôi đã phân tích cú pháp từ
{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }
30 - Tên của khóa từ điển chứa các giá trị chúng tôi muốn trích xuất
{ "persons": [ { "name": "erik", "age": 38 }, { "name": "john", "age": 45 }, { "name": "rob", "age": 14 } ] }9Đầu ra của
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
9Bất kể vị trí của khóa "văn bản" trong JSON, hàm này sẽ trả về mọi giá trị cho ví dụ về "khóa. " Đây là chức năng của chúng tôi trong hành động
$ pip3 install jmespath $ python3 Python 3.8.2 [default, Jul 16 2020, 14:00:26] >>> import jmespath >>> j = { "people": [{ "name": "erik", "age": 38 }] } >>> jmespath.search["people[*].age", j] [38] >>>1chính. py
Chạy chức năng này sẽ dẫn đến đầu ra sau
$ pip3 install jmespath $ python3 Python 3.8.2 [default, Jul 16 2020, 14:00:26] >>> import jmespath >>> j = { "people": [{ "name": "erik", "age": 38 }] } >>> jmespath.search["people[*].age", j] [38] >>>2Đầu ra của
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
6Oh fiddle me woods. Bởi vì Google API luân phiên giữa khoảng cách và thời lượng chuyến đi, mọi giá trị khác luân phiên giữa khoảng cách và thời gian [chúng ta có thể tạm dừng để đánh giá cao thiết kế tồi tệ này không? Có vô số cách tốt hơn để cấu trúc phản hồi này]. Đừng sợ, một số Python đơn giản có thể giúp chúng tôi chia danh sách này thành hai danh sách
$ pip3 install jmespath $ python3 Python 3.8.2 [default, Jul 16 2020, 14:00:26] >>> import jmespath >>> j = { "people": [{ "name": "erik", "age": 38 }] } >>> jmespath.search["people[*].age", j] [38] >>>4Phân tích mọi giá trị khác từ phản hồi JSON
Thao tác này sẽ lấy một danh sách của chúng ta và chia nó thành hai danh sách, xen kẽ giữa chẵn và lẻ
$ pip3 install jmespath $ python3 Python 3.8.2 [default, Jul 16 2020, 14:00:26] >>> import jmespath >>> j = { "people": [{ "name": "erik", "age": 38 }] } >>> jmespath.search["people[*].age", j] [38] >>>5Đầu ra
Sáng tạo với danh sách
Một chủ đề phổ biến mà tôi gặp phải khi trích xuất danh sách giá trị từ các đối tượng JSON như thế này là danh sách giá trị tôi trích xuất có liên quan rất nhiều. Trong ví dụ trên, với mỗi khoảng thời gian, chúng ta có một khoảng cách đi kèm, đó là cơ sở một đối một. Hãy tưởng tượng nếu chúng ta muốn liên kết những giá trị này bằng cách nào đó
Để sử dụng một ví dụ tốt hơn, gần đây tôi đã sử dụng hàm
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
9 của mình để tìm nạp danh sách tên cột và kiểu dữ liệu của chúng từ lược đồ cơ sở dữ liệu. Dưới dạng các danh sách riêng biệt, dữ liệu trông giống như thế này{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
0Hai danh sách liên quanHai danh sách này có liên quan trực tiếp với nhau; . Làm thế nào điều này có thể hữu ích?
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
1Nhép hai danh sách vào một từ điểnTôi thích nghĩ rằng họ gọi nó là zip vì nó giống như kéo khóa kéo lên, trong đó mỗi bên của khóa kéo là một danh sách. Điều này xuất ra một từ điển trong đó danh sách 1 đóng vai trò là khóa và danh sách 2 đóng vai trò là giá trị
{
"destination_addresses": [
"Philadelphia, PA, USA"
],
"origin_addresses": [
"New York, NY, USA"
],
"rows": [{
"elements": [{
"distance": {
"text": "94.6 mi",
"value": 152193
},
"duration": {
"text": "1 hour 44 mins",
"value": 6227
},
"status": "OK"
}]
}],
"status": "OK"
}
2Danh sách nén dẫn đến từ điểnVà ở đó bạn có nó folks. một đoạn mã miễn phí để sao chép và bí mật giả vờ như bạn đã viết mãi mãi. Tôi đã đưa chức năng này lên Github Gists, nếu điều đó làm bạn hài lòng