Sử dụng jq để trích xuất các giá trị và định dạng trong CSV


58

Tôi có tệp JSON dưới đây:

{
"data": [
    {
        "displayName": "First Name",
        "rank": 1,
        "value": "VALUE"
    },
    {
        "displayName": "Last Name",
        "rank": 2,
        "value": "VALUE"
    },
    {
        "displayName": "Position",
        "rank": 3,
        "value": "VALUE"
    },
    {
        "displayName": "Company Name",
        "rank": 4,
        "value": "VALUE"
    },
    {
        "displayName": "Country",
        "rank": 5,
        "value": "VALUE"
    },
]
}

Tôi muốn có một tệp CSV ở định dạng này:

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE, VALUE

Điều này có thể bằng cách chỉ sử dụng jq? Tôi không có bất kỳ kỹ năng lập trình nào.


1
Tôi đã cung cấp câu trả lời bên dưới, nhưng bây giờ tôi đang xem xét kỹ hơn câu hỏi của bạn và tôi không thể không tự hỏi - GIÁ TRỊ thứ 6 được cho là từ đâu?
mikeerv


Cũng liên quan stackoverflow.com/q
3260857/168034

Câu trả lời:


50

jq có bộ lọc, @csv, để chuyển đổi một mảng thành chuỗi CSV. Bộ lọc này tính đến hầu hết các phức tạp liên quan đến định dạng CSV, bắt đầu bằng dấu phẩy được nhúng trong các trường. (jq 1.5 có bộ lọc tương tự, @tsv, để tạo các tệp giá trị được phân tách bằng tab.)

Tất nhiên, nếu các tiêu đề và giá trị đều được đảm bảo không có dấu phẩy và dấu ngoặc kép, thì có thể không cần sử dụng bộ lọc @csv. Nếu không, nó có lẽ sẽ tốt hơn để sử dụng nó.

Ví dụ: nếu 'Tên công ty' là 'Smith, Smith và Smith' và nếu các giá trị khác như được hiển thị bên dưới, việc gọi jq với tùy chọn "-r" sẽ tạo CSV hợp lệ:

$ jq -r '.data | map(.displayName), map(.value) | @csv' so.json2csv.json
"First Name","Last Name","Position","Company Name","Country"
"John (""Johnnie"")","Doe","Director, Planning and Posterity","Smith, Smith and Smith","Transylvania"

3
Tôi đã có thể 'jq somestuff | bản đồ (.) | @csv ', rất tiện dụng! Cảm ơn
flickerfly

3
Ví dụ của bạn sẽ đặt tất cả các tên hiển thị trên dòng đầu tiên và tất cả các giá trị trên dòng thứ hai, thay vì có một dòng trên mỗi bản ghi.
Brian Gordon

33

Tôi thích làm cho mỗi bản ghi một hàng trong CSV của tôi.

jq '.data | map([.displayName, .rank, .value] | join(", ")) | join("\n")'

2
Nếu .value là một số thì sao? Tôi nhận được lỗi "chuỗi và số không thể được thêm vào"
Cos

2
@Cos một cái gì đó giống như .value|tostringthay vì .valuetrong ví dụ trên
matheeeny

4
@Cos, tôi thấy dấu ngoặc đơn là bắt buộc. (.value|tostring)
ciscogambo

Ngoài ra, sử dụng jq -rđể loại bỏ các trích dẫn
Clay

30

Chỉ với tập tin này, bạn có thể làm một cái gì đó như:

<testfile jq -r '.data | map(.displayName), map(.value) | join(", ")'

Các .nhà điều hành lựa chọn một trường từ một đối tượng / băm. Vì vậy, chúng tôi bắt đầu với .data, trả về mảng với dữ liệu trong đó. Sau đó, chúng tôi ánh xạ qua mảng hai lần, đầu tiên chọn displayName, sau đó chọn giá trị, cung cấp cho chúng tôi hai mảng chỉ với các giá trị của các khóa đó. Đối với mỗi mảng, chúng ta nối các phần tử với "," tạo thành hai dòng. Đối -rsố nói jqkhông trích dẫn chuỗi kết quả.

Nếu tệp thực tế của bạn dài hơn (nghĩa là có nhiều mục nhập cho nhiều người), bạn có thể sẽ cần một cái gì đó phức tạp hơn một chút.


Nó không làm việc cho tôi. Trong một chủ đề liên quan, câu trả lời stackoverflow.com/questions/32960857/ cấp vừa hoạt động vừa được giải thích rất rõ!
herve

10

Tôi thấy jqkhó khăn để quấn đầu mình. Đây là một số Ruby:

ruby -rjson -rcsv -e '
  data = JSON.parse(File.read "file.json")
  data["data"].collect {|item| [item["displayName"], item["value"]]}
              .transpose
              .each {|row| puts row.to_csv}
'
First Name,Last Name,Position,Company Name,Country
VALUE,VALUE,VALUE,VALUE,VALUE

Trình phân tích cú pháp JSON của ruby ​​đã viết về dấu phẩy ở trước dấu ngoặc đóng.


2

Vì bạn đã gắn thẻ này pythonvà giả sử tên của jsontệp làx.json

import os, json
with open('x.json') as f:
    x  = json.load(f)
    print '{}{}{}'.format(', '.join(y['displayName'] for y in x['data']), os.linesep,
             ', '.join(y['value'] for y in x['data']))
First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE

1

Mặc dù tôi đã phải xóa dấu phẩy cuối cùng trong đầu vào ví dụ của bạn để làm cho nó hoạt động vì jqphàn nàn về việc mong đợi một phần tử mảng khác, điều này:

INPUT | jq -r '[.[][].displayName], [.[][].value]| join(", ")'

... đã cho tôi ...

First Name, Last Name, Position, Company Name, Country
VALUE, VALUE, VALUE, VALUE, VALUE

Làm thế nào nó hoạt động trong một tóm tắt:

  1. Tôi đi qua cấp độ thứ ba của các đối tượng dữ liệu bằng cách sử dụng []biểu mẫu và .dotký hiệu trường chỉ mục trống .
  2. Khi đủ sâu, tôi chỉ định các trường dữ liệu tôi muốn theo tên như thế nào .[][].displayName.
  3. Tôi đảm bảo rằng các trường mong muốn của tôi được tự liên kết bằng cách trả về chúng dưới dạng các đối tượng mảng riêng biệt như [.[][].displayName], [.[][].value]
  4. Và sau đó dẫn các đối tượng đó đến join(", ")hàm được nối thành các thực thể riêng biệt.

Trong thực tế, việc làm [.field]chỉ là một cách khác map(.field)nhưng điều này cụ thể hơn một chút ở chỗ nó chỉ định mức độ sâu để lấy dữ liệu mong muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.