【Jqコマンド活用法】JSONオブジェクトを再構成〜フィールドの一部を書き換えてみる


※ 当ページには【広告/PR】を含む場合があります。
2022/10/01
【jqコマンド実用編】押さえておきたいデータ入出力のためのjqのコマンドオプションまとめ
Jq&AwkコマンドでJSONファイル⇔CSVファイルに相互変換する方法を考察
蛸壺の技術ブログ|JSONオブジェクトを再構成〜フィールドの一部を書き換えてみる

「Jqコマンド」を使っていると大抵のJSONファイルの自由度の高い編集がLinuxのターミナルから可能になります。

他方で、Jqコマンドの独特なクエリ構文になれるまでは、どのようにJSONを扱っていいものか迷うことも多々あります。

今回は、既存のJSONオブジェクトを読み込んで、その一部のフィールドを書き換え・更新するテクニックを考えてみます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】シェルスクリプトをこれから学びたい人のためのオススメ書籍&教材特集

Jqのアサインメント構文を理解する

まずは本題に入る前に、Jqコマンドの「アサインメント(Assigment)」を良く理解しておく必要があります。

Assigment - jq

アサインメント操作の基本は、
『|=』オペレーターであり、公式では「Update Operator」とか「Update Assigment」として紹介されています。

            
            用法:
    <左のJSON要素> |= <右のJSON要素>

意味:
    左のJSON要素を右のJSON要素で置き換える
        
簡単な例で確かめると、

            
            $ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq '.pet.feature |= "三毛"'
{
  "pet": {
    "name": "たま",
    "kind": "ネコ",
    "feature": "三毛"
  }
}
        
またjq1.6以降からは、右の要素が複数していされていた場合、指定された要素のうちで最初のもの(1.6以前のバージョンでは最後のもの)で置き換わるというルールに変更されています。

            
            #jq1.6以降のバージョン
$ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq '.pet.feature |= ("白毛","栗毛","三毛")'
{
  "pet": {
    "name": "たま",
    "kind": "ネコ",
    "feature": "白毛"
  }
}

#jq1.5以前のバージョン
$ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq '.pet.feature |= ("白毛","栗毛","三毛")'
{
  "pet": {
    "name": "たま",
    "kind": "ネコ",
    "feature": "三毛"
  }
}
        
未定義のフィールドキーで指定すると、要素の書き換えではなく、新しい要素が追加されます。

            
            $ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq '.pet.age |= "5歳"'
{
  "pet": {
    "name": "たま",
    "kind": "ネコ",
    "feature": "ブチ",
    "age": "5歳"
  }
}
        
右の要素を空の要素を意味するemptyで指定した場合、左の要素はキー-バリューのペアごと消去されます(=deleteメソッド同等)。

            
            $ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq '.pet.name |= empty'
{
  "pet": {
    "kind": "ネコ",
    "feature": "ブチ"
  }
}

#以下でも同じ
$ echo '{"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}' | jq 'del(.pet.name)'
{
  "pet": {
    "kind": "ネコ",
    "feature": "ブチ"
  }
}
        


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】シェルスクリプトをこれから学びたい人のためのオススメ書籍&教材特集

Jqの変数のシンボリックバインドを利用する

jqコマンドで、JSONオブジェクトをより高度に編集するためには、「シンボリックバインドオペレーター(Symbolic Binding Operator)」を駆使する必要が出てきます。

Variable / Symbolic Binding Operator

シンボリックバインドによって、変数としてJSONオブジェクトを利用することで、自由自在にJSONを扱うことが可能になります。

            
            用法:
    <バインドしたいJSON要素> as $<識別子> | ...

意味:
    再利用したいJSON要素に識別子を使ってマークする
        
識別子は、「$」を最初に使って定義する必要があります。

            
            $ echo '{"owner":{"name":"山田 タロヲ","age":37,"occupation":"漁師","hobby":[{"name":"登山","carrier":"10年"},{"name":"料理","carrier":"4年"}],"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}}' | \
jq '.owner.pet as $pet | $pet'
{
  "name": "たま",
  "kind": "ネコ",
  "feature": "ブチ"
}
        
シンボリックバインドを利用することで、指定したキー(ここでは.owner.pet)のもつバリューが$petに格納されることで、それ以降の処理で利用することができます。


合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】シェルスクリプトをこれから学びたい人のためのオススメ書籍&教材特集

jqコマンドによる柔軟なJSONオブジェクト編集

上述してきた、「更新オペレーター」「シンボリックバインドオペレーター」を組み合わせることで、JSONオブジェクトの編集の自由度が大幅に広がります。

例えば、先程の例だと、飼い主と飼い猫の関係を入れ替えることを考えてみましょう。

この操作をワンライナーのjqスクリプトで表現すると以下のようになるでしょう。

            
            $ echo '{"owner":{"name":"山田 タロヲ","age":37,"occupation":"漁師","hobby":[{"name":"登山","carrier":"10年"},{"name":"料理","carrier":"4年"}],"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}}' |\
jq '(.owner.pet |= empty).owner as $human | .owner.pet as $pet | {"owner":$pet} | .owner.pet |= $human'
{
  "owner": {
    "name": "たま",
    "kind": "ネコ",
    "feature": "ブチ",
    "pet": {
      "name": "山田 タロヲ",
      "age": 37,
      "occupation": "漁師",
      "hobby": [
        {
          "name": "登山",
          "carrier": "10年"
        },
        {
          "name": "料理",
          "carrier": "4年"
        }
      ]
    }
  }
}
        
一見かなり複雑なJSONオブジェクトの操作を行っているように見えますが、jqスクリプトを分解して一つ一つ考えてみると、分かりやすいかもしれません。

            
            $ echo '{"owner":{"name":"山田 タロヲ","age":37,"occupation":"漁師","hobby":[{"name":"登山","carrier":"10年"},{"name":"料理","carrier":"4年"}],"pet":{"name":"たま","kind":"ネコ","feature":"ブチ"}}}' |\
jq '
  #👇飼い主の情報だけをバインド
  (.owner.pet |= empty).owner as $human |
  #👇ペットの情報をバインド
  .owner.pet as $pet |
  #👇JSONオブジェクト全体をペットの情報から新規オブジェクトで置き換え
  {"owner":$pet} |
  #👇新規キーに飼い主の情報をセット
  .owner.pet |= $human
'
        
やっていることは、更新オペレーターとシンボリックバインドオペレーターで、フィールドの値を付けたり剥がしたりしているだけです。

他には配列などから操作する「map」オペレーターも組み合わせると、非常に柔軟なJSONオブジェクトの操作がワンライナーのjqスクリプトで実現できます。

ちょっとしたJSONファイルを編集するだけなら、jqスクリプトちょちょいと作成しておくと、いざという時に便利に使えることでしょう。

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログ【効果的学習法レポート】シェルスクリプトをこれから学びたい人のためのオススメ書籍&教材特集