一貫した順序を取得するために、JSONファイルのすべての配列を深さ優先ソート

一貫した順序を取得するために、JSONファイルのすべての配列を深さ優先ソート

リビジョン管理システムでは、JSON形式で一部のシステムの構成を追跡しています。

残念なことに、構成は一部のプライベートソース専用コマンドを使用して取得され、オブジェクトと配列の順序がややランダムであるため、出力は1つの実行から次の実行に変わります。

出力されます:

{
  "fru": [
    {
      "name": "foo",
      "attr": [
         {"name": "colour", "value": "blue"},
         {"name": "length", "value": 12}
      ]
    },
    {
      "name": "bar",
      "attr": [
         {"name": "colour", "value": "red"},
         {"name": "length", "value": 1}
      ]
    }
  ],
  "tags": ["x", "y"]
}

次回:

{
  "tags": ["y", "x"],
  "fru": [
    {
      "name": "bar",
      "attr": [
         {"name": "length", "value": 1},
         {"name": "colour", "value": "red"}
      ]
    },
    {
      "name": "foo",
      "attr": [
         {"name": "colour", "value": "blue"},
         {"name": "length", "value": 12}
      ]
    }
  ]
}

これは、PoVの観点からはgit diff全く同じシステムであっても、1つの実行から次の実行にすべてが変更されることを意味します。

すべての配列の順序は重要ではありません。オブジェクト属性の順序も重要ではありません。したがって、オブジェクトと配列のプロパティとメンバーが同じ順序になるようにこの出力を後処理することができれば、システムが変更されないときに出力が変更されず、私が見る変更が変更されるgit diffことを保証できます。システムの変化を反映する可能性が高くなります。

jq -S私は以下から多くの恩恵を受けました:

  • オブジェクト内の属性のソート
  • 個々のオブジェクトプロパティと配列メンバーを別々の行に配置します(git diff行ベース)。

上記の例では、以下を提供します。

{
  "fru": [
    {
      "attr": [
        {
          "name": "colour",
          "value": "blue"
        },
        {
          "name": "length",
          "value": 12
        }
      ],
      "name": "foo"
    },
    {
      "attr": [
        {
          "name": "colour",
          "value": "red"
        },
        {
          "name": "length",
          "value": 1
        }
      ],
      "name": "bar"
    }
  ],
  "tags": [
    "x",
    "y"
  ]
}

そして:

{
  "fru": [
    {
      "attr": [
        {
          "name": "length",
          "value": 1
        },
        {
          "name": "colour",
          "value": "red"
        }
      ],
      "name": "bar"
    },
    {
      "attr": [
        {
          "name": "colour",
          "value": "blue"
        },
        {
          "name": "length",
          "value": 12
        }
      ],
      "name": "foo"
    }
  ],
  "tags": [
    "y",
    "x"
  ]
}

これは良いですが、配列がソートされていないため(理解できません)、まだ実装されていません。

実際のファイルは、より多くの配列を含む他のオブジェクト配列の配列を含むより複雑です。

私の考えは、値のJSON文字列表現に基づいて最も深い配列から始まり、すべての配列を並べ替えることでこの問題を解決することです。たとえば、文字列は前にソートされているため、前.fru[0].attrにソートします。{"name": "colour", "value": "blue"}{"name": "length", "value": 12}{"name":"colour","value":"blue"}長さまず、.fru配列はfoobeforeでソートされます。bar(属性は{"attr":[..."blue"...アルファベット順に前方にattr移動するためですname) sort before です{"attr":[..."red"...

以下を使用して、すべての配列のパス(深さ優先)を取得できます。

$ jq -c '[paths(arrays)]|reverse' a
[["tags"],["fru",1,"attr"],["fru",0,"attr"],["fru"]]

配列メンバーのJSON文字列表現に基づいて配列をソートできます。

jq '.array|=sort_by(tojson)'

しかし、2つを組み合わせて最初から返されたすべての配列に2番目を適用するにはどうすればよいですか?

それとも、順序を一貫して維持するためにJSONを後処理するより良い方法はありますか?

最高のツールでない場合は、モジュールやRuby / Pythonと同等のツールをjq考えてみましょう。perlJSON

答え1

このwalk()機能はこのユースケースに最適なようです。指定されたフィルタを各JSON要素にボトムアップ方式で繰り返し適用し、結果を返します。実際、すべての配列をソートするのがその例の1つです。文書:

$ jq -S 'walk(if type == "array" then sort else . end)' a
{
  "fru": [
    {
      "attr": [
        {
          "name": "colour",
          "value": "blue"
        },
        {
          "name": "length",
          "value": 12
        }
      ],
      "name": "foo"
    },
    {
      "attr": [
        {
          "name": "colour",
          "value": "red"
        },
        {
          "name": "length",
          "value": 1
        }
      ],
      "name": "bar"
    }
  ],
  "tags": [
    "x",
    "y"
  ]
}

必要なのは、一貫したソート順序だけであれば、その方法が効果的です(jqのsortフィルタは、オブジェクトを含むすべての要素の決定的なソートを定義するため)。ただし、文字列表現に基づいて配列要素を並べ替える場合は、次のことができます。もちろん。sortsort_by(tojson)

しかし、これをテキストベースの比較を実行するのではなく、2つのJSONドキュメントを構造的に比較する(たとえば、オブジェクトのキー順序を無視する)JSON diffツールと組み合わせると便利ですが、これは別の問題です。

関連情報