개정 관리 시스템에서 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
배열은 foo
before로 정렬됩니다. 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
고려해 보겠습니다 .perl
JSON
답변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 도구와 결합하는 것이 유용할 수 있지만 이는 또 다른 문제입니다.