jqを使用してjson形式を変更する

jqを使用してjson形式を変更する

次の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だけを使用できますか?

答え1

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キーの値を配列に取得します。また、配列内のサブキーのすべての値を選択し、それらを区切り文字でスペースで連結します。namevardate1date

data@csv次に、出力のために各文字列を参照し、要素をコンマで区切る各要素に対して作成された配列と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説明のため表現に若干の空気を入れて挿入しました。

.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パーサーは名前で列を取得するのに問題はありません。

関連情報