일관된 순서를 얻기 위해 JSON 파일의 모든 배열을 깊이 우선 정렬

일관된 순서를 얻기 위해 JSON 파일의 모든 배열을 깊이 우선 정렬

개정 관리 시스템에서 JSON 형식으로 일부 시스템의 구성을 추적하고 있습니다.

불행하게도 구성은 일부 비공개 소스 독점 명령을 사용하여 검색되며 개체와 배열의 순서가 다소 무작위이기 때문에 출력이 한 실행에서 다음 실행으로 변경됩니다.

일단 출력됩니다:

{
  "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때 완전히 동일한 시스템이라 할지라도 한 실행에서 다음 실행으로 모든 것이 변경된다는 것을 의미합니다.

모든 배열에서 순서는 중요하지 않습니다. 객체 속성의 순서도 중요하지 않습니다. 따라서 개체와 배열의 속성과 멤버가 동일한 순서가 되도록 이 출력을 사후 처리할 수 있다면 시스템이 변경되지 않을 때 출력이 변경되지 않고 내가 보는 변경 사항이 변경 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)'

하지만 두 가지를 결합하여 첫 번째에서 반환된 모든 배열에 두 번째를 적용하려면 어떻게 해야 합니까?

아니면 순서를 일관되게 유지하기 위해 JSON을 사후 처리하는 더 좋은 방법이 있습니까?

최고의 도구가 아니라면 모듈 이나 Ruby/Python과 동등한 도구를 jq고려해 보겠습니다 .perlJSON

답변1

walk()기능은 이 사용 사례에 완벽한 것 같습니다. 지정된 필터를 각 JSON 요소에 상향식 방식으로 반복적으로 적용하고 결과를 반환합니다. 실제로 모든 배열을 정렬하는 것이 그 예 중 하나입니다.문서:

$ 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필터는 객체를 포함한 모든 요소의 결정적 정렬을 정의하므로). 그러나 문자열 표현을 기준으로 배열 요소를 정렬하려는 경우에는 다음을 수행할 수 있습니다. 물론 sort.sort_by(tojson)

그런데, 이것을 텍스트 기반 비교를 수행하는 대신 두 개의 JSON 문서를 구조적으로 비교하는(예: 객체의 키 순서 무시) JSON diff 도구와 결합하는 것이 유용할 수 있지만 이는 또 다른 문제입니다.

관련 정보