Estou acompanhando a configuração de algum sistema no formato JSON em um sistema de controle de revisão.
Infelizmente, essa configuração é recuperada usando algum comando proprietário de código fechado, e a saída muda de uma execução para a próxima, pois a ordem nos objetos e matrizes é mais ou menos aleatória.
Uma vez, ele produzirá:
{
"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"]
}
E da próxima vez:
{
"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}
]
}
]
}
Isso significa que, do ponto de vista de git diff
, tudo muda de uma execução para outra, mesmo que seja exatamente o mesmo sistema.
Em todas as matrizes, a ordem não é relevante. A ordem também não importa nos atributos dos objetos. Então, se eu pudesse pós-processar essa saída para que os objetos e arrays tivessem seus atributos e membros em uma ordem consistente, eu garantiria que a saída não mudaria quando o sistema não mudasse e as mudanças vistas por git diff
seriam mais provavelmente refletirá as mudanças no sistema.
jq -S
me ajuda bastante:
- classificando atributos dentro de objetos
- colocar atributos de objetos separados e membros da matriz em linhas separadas (
git diff
é baseado em linhas).
Para o exemplo acima, isso me dá:
{
"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"
]
}
E:
{
"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"
]
}
Isso é melhor, mas ainda não está lá, pois os arrays não estão classificados (compreensivelmente).
Observe que o arquivo da vida real é mais complexo com arrays contendo outros arrays de objetos contendo mais arrays...
Meu pensamento para resolver isso é classificar todas as matrizes, começando pelas mais profundas com base na representação da string JSON dos valores, por exemplo, .fru[0].attr
classificada com {"name": "colour", "value": "blue"}
antes {"name": "length", "value": 12}
porque a {"name":"colour","value":"blue"}
string é classificada antes do comprimento um e, em seguida, a .fru
matriz classificada com foo
antes bar
porque {"attr":[..."blue"...
(com o attr
atributo movido antes name
em ordem alfabética) é classificado antes {"attr":[..."red"...
.
Posso obter os caminhos de todos os arrays, primeiro a profundidade com:
$ jq -c '[paths(arrays)]|reverse' a
[["tags"],["fru",1,"attr"],["fru",0,"attr"],["fru"]]
Posso classificar uma matriz com base na representação da string JSON de seus membros com:
jq '.array|=sort_by(tojson)'
Mas como combinar os dois para aplicar o segundo a todos os arrays retornados pelo primeiro?
Ou existe uma maneira melhor de pós-processar esse JSON para que o pedido permaneça consistente?
Se jq
não for a melhor ferramenta para isso, ficarei feliz em considerar os módulos perl
do JSON
ou os equivalentes Ruby/python.