以下3つの配列からなるjsonから、csvを作成(変換)してみます。

sample1.json

[
    {
        "name": "name1",
        "ip":
        [
            "192.168.0.1/32",
            "192.168.0.2/32"
        ],
        "dstname": "dst1"
    },
    {
        "name": "name2",
        "ip":
        [
            "192.168.1.1/32",
            "192.168.1.2/32"
        ],
        "dstname": "dst2"
    },
    {
        "name": "name3",
        "dstname": "dst3"
    }
]

まずは、簡単な所から。

各配列の「.name」と「.dstname」の一覧をCSV化

$ cat sample1.json | jq -r '.[] | 
[
.name,
.dstname
] | @csv'

<出力>
"name1","dst1"
"name2","dst2"
"name3","dst3"

.ip の値もcsvに出力してみましょう。.ipは配列なので、複数のIPが記載されている場合はスペース区切りで結合し、IPの記載がない場合は空文字に変換。

$ cat sample1.json | jq -r '.[] |
[
.name,
(if .ip then .ip | join(" ") else "" end),
.dstname
] | @csv'

<出力>
"name1","192.168.0.1/32 192.168.0.2/32","dst1"
"name2","192.168.1.1/32 192.168.1.2/32","dst2"
"name3","","dst3"

次は、jsonの階層が深くなって、最上位の階層にメタ情報が付与されているjsonを考えてみます。

sample2.json

[
    {
        "rulename": "firstcollection",
        "rules":
        [
            {
                "name": "name1",
                "ip":
                [
                    "192.168.0.1/32",
                    "192.168.0.2/32"
                ],
                "dstname": "dst1"
            },
            {
                "name": "name2",
                "ip":
                [
                    "192.168.1.1/32",
                    "192.168.1.2/32"
                ],
                "dstname": "dst2"
            },
            {
                "name": "name3",
                "dstname": "dst3"
            }
        ]
    }
]

ここでは、.rulename というメタ情報もcsvの1カラムに出力することを目標とします。

出力イメージ

"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

案1 .rulenameと.rulesを結合

.rulename と .rules 配下の各配列を結合後、csvに変換します。

まずは結合だけをやってみます。rulename: と rule: は結合後のキーの名前なので任意の名前を指定できますが、今回は元のjsonのキー名をほぼそのまま使っています。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, rule: .rules[] }'
{
"rulename": "firstcollection",
"rule": {
"name": "name1",
"ip": [
"192.168.0.1/32",
"192.168.0.2/32"
],
"dstname": "dst1"
}
}

{
"rulename": "firstcollection",
"rule": {
"name": "name2",
"ip": [
"192.168.1.1/32",
"192.168.1.2/32"
],
"dstname": "dst2"
}
}

{
"rulename": "firstcollection",
"rule": {
"name": "name3",
"dstname": "dst3"
}
}

.rulename と .rules[] の各配列が結合されました。

余談ですが、結合時に欲張って .rules[].name や .rule[].dstname などを指定すると、それぞれの値が組み合わされて(直積)、意味のない集合になってしまうので注意が必要です。今回は3要素しか結合してないのですぐに処理が終わりましたが、要素が増えると爆発的に組み合わせが増えてCPUを食いつぶします。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, name: .rules[].name, dstname: .rules[].dstname }'
{
"rulename": "firstcollection",
"name": "name1",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name1", ★ name1とdst2という異なる配列の値が結合される
"dstname": "dst2" ★
}
{
"rulename": "firstcollection",
"name": "name1",
"dstname": "dst3"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst2"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst3"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst2"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst3"
}

話を元に戻します。結合後のjsonをcsvに変換するのは、最初と同じです。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, rule: .rules[] } |
[
.rulename, .rule.name,
(if .rule.ip then .rule.ip | join(" ") else "" end),
.rule.dstname
] | @csv'

<出力>
"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

案2 .rulenameを変数に入れて、配列化時に埋め込む

.rulenameと.rulesを結合するのではなく、.rules内に.rulenameの値を入れ込む方法でも実現できます。以下は .rulename の値を $rulename という変数に入れ、[ ] 内で再利用しています。

$ cat sample2.json | jq -r '.[] | .rulename as $rulename | .rules[] | 
[
$rulename,
.name,
(if .ip then .ip | join(" ") else "" end),
.dstname
] | @csv'

<出力>
"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

こちらの方が記述がシンプルですね。

案3 文字列結合を使って、自力でcsv化

.rulename と、.rules[] の各要素を、”+” を使って手動で結合する方法(荒業?)です。@csv を使っていないためcsvの値がダブルクォーテーションで囲まれていないですが、これでも目的は達成できそうです。

$ cat sample2.json | jq -r '.[] |
.rulename + "," +
(
.rules[] |
.name + "," +
(if .ip then .ip | join(" ") else "" end) + "," +
.dstname
)'

<出力>
firstcollection,name1,192.168.0.1/32 192.168.0.2/32,dst1
firstcollection,name2,192.168.1.1/32 192.168.1.2/32,dst2
firstcollection,name3,,dst3