Bash

使用 jq 重新格式化 json

  • February 3, 2022

我有以下 JSON 文件:

{ data : [
 {
  "name" : "name1"
  "date" : [
    {
     "date1" : "aaa",
     "date2" : "bbb"
    },
    {
     "date1" : "ccc",
     "date2" : "ddd"
    },
    {
     "date1" : "eee",
     "date2" : "fff"
    },
    "var" : "ggg"
},
{
  "name" : "name2"
  "date" : [
    {
     "date1" : "hhh",
     "date2" : "iii"
    },
    {
     "date1" : "jjj",
     "date2" : "kkk"
    },
    "var" : "lll"
 }
]
}

我想要這種格式的 CSV 文件:

name, date, var
name1, aaa ccc eee, ggg
name2, hhh jjj, lll

僅使用jq可以做到這一點嗎?

假設您的 JSON 文件格式正確,例如您呈現的文本的以下修改變體:

{
 "data": [
   {
     "name": "name1",
     "date": [
       {
         "date1": "aaa",
         "date2": "bbb"
       },
       {
         "date1": "ccc",
         "date2": "ddd"
       },
       {
         "date1": "eee",
         "date2": "fff"
       }
     ],
     "var": "ggg"
   },
   {
     "name": "name2",
     "date": [
       {
         "date1": "hhh",
         "date2": "iii"
       },
       {
         "date1": "jjj",
         "date2": "kkk"
       }
     ],
     "var": "lll"
   }
 ]
}

然後我們可以執行以下操作:

$ jq -r '[ "name", "date", "var" ], (.data[] | [.name, ([.date[].date1] | join(" ")), .var]) | @csv' file
"name","date","var"
"name1","aaa ccc eee","ggg"
"name2","hhh jjj","lll"

jq表達式,用更多的空氣來顯示結構:

[ "name", "date", "var" ],
(
   .data[] |
   [
     .name,
     ( [ .date[].date1 ] | join(" ") ),
     .var
   ]
) |
@csv

這首先將 CSV 標頭創建為字元串數組。然後它遍歷頂層data數組,將namevar鍵的值提取到一個數組中。它還挑選出數組date1中子鍵的所有值date,並將它們與空格連接起來作為分隔符。

然後使用 CSV 標頭數組以及為每個data元素構造的數組進行處理以輸出@csv,它引用每個字元串並用逗號分隔元素。


另一種變體:

box% jq -r '.data[].date |= ( [.[].date1] | join(" ") ) | [ (.data[0] | keys[]) ], (.data[]| [.[]]) | @csv' file
"date","name","var"
"name1","aaa ccc eee","ggg"
"name2","hhh jjj","lll"

這會預處理數據,以便第一個管道之後的表達式部分獲得以下輸入:

{
 "data": [
   {
     "name": "name1",
     "date": "aaa ccc eee",
     "var": "ggg"
   },
   {
     "name": "name2",
     "date": "hhh jjj",
     "var": "lll"
   }
 ]
}

第一個管道之後的程式碼只需提取 CSV 標頭的鍵,然後收集值,而不必擔心鍵的名稱。

再一次,jq為了說明而插入了一點 mor 空氣的表達式:

.data[].date |= ( [ .[].date1 ] | join(" ") ) |
[ (.data[0] | keys[]) ],
( .data[] | [.[]] ) |
@csv

時髦的外觀.data[] | [.[]]創建了每個元素中所有鍵的值的數組data

如果擔心.[]應用於非數組可能會以與keys產生的順序不同的順序提取值,那麼可以使用

.data[].date |= ( [ .[].date1 ] | join(" ")) |
(.data[0] | keys) as $keys |
$keys,
( .data[]| [ .[$keys[]] ] ) |
@csv

即,將標題鍵提取到變數$keys中,使用它來創建 CSV 標題data從數組中的元素中提取數據。列可能是隨機順序,但至少標題和其餘數據是相同的隨機順序,CSV 解析器按名稱提取列沒有問題。

引用自:https://unix.stackexchange.com/questions/689122