You may want something like this:
xq -x -S 'walk(if type == "array" then sort_by(."@name") else . end)' file
This uses -S
(or --sort-keys
) to sort the keys (XML tags) using their names so that the module
keys come before the property
keys.
It then uses the recursive walk()
function to apply sort_by()
to each array, sorting the elements of each array based on the value of the name
attribute (written ."@name"
).
This walk()
usage is almost identical to an example in the jq
manual.
This would produce the following output:
<module name="Checker">
<module name="FileTabCharacter"></module>
<module name="NewlineAtEndOfFile"></module>
<module name="TreeWalker">
<module name="AvoidDoubleBraceInitialization"></module>
<module name="AvoidNoArgumentSuperConstructorCall"></module>
<module name="ClassTypeParameterName"></module>
<module name="InterfaceTypeParameterName"></module>
<module name="LambdaParameterName"></module>
<module name="MethodTypeParameterName"></module>
<module name="OneTopLevelClass"></module>
<module name="OuterTypeFilename"></module>
<module name="PackageName"></module>
<module name="PatternVariableName"></module>
<module name="RecordComponentName"></module>
<module name="RecordTypeParameterName"></module>
<module name="TypeName">
<property name="format" value="^[A-Z][_a-zA-Z0-9]*$"></property>
</module>
</module>
<property name="fileExtensions" value="java"></property>
<property name="severity" value="error"></property>
</module>
Note that xq
writes out the end tags explicitly, even for empty nodes. If you want to fix that (so that <tag attr="..."></tag>
is changed to <tag attr="..."/>
), pass the result through xmlstarlet fo
or xmlstarlet format
.
As a reference, the JSON document that the original XML document is translated into (with no sorting whatsoever) and to which the jq
expression is applied is the equivalent of the following:
{
"module": {
"@name": "Checker",
"module": [
{ "@name": "NewlineAtEndOfFile" },
{ "@name": "FileTabCharacter" },
{
"@name": "TreeWalker",
"module": [
{ "@name": "PackageName" },
{ "@name": "ClassTypeParameterName" },
{ "@name": "InterfaceTypeParameterName" },
{ "@name": "MethodTypeParameterName" },
{ "@name": "LambdaParameterName" },
{ "@name": "PatternVariableName" },
{ "@name": "RecordComponentName" },
{ "@name": "RecordTypeParameterName" },
{
"@name": "TypeName",
"property": { "@name": "format", "@value": "^[A-Z][_a-zA-Z0-9]*$"
},
{ "@name": "AvoidDoubleBraceInitialization" },
{ "@name": "AvoidNoArgumentSuperConstructorCall" },
{ "@name": "OneTopLevelClass" },
{ "@name": "OuterTypeFilename" }
]
}
],
"property": [
{ "@name": "severity", "@value": "error" },
{ "@name": "fileExtensions", "@value": "java" }
]
}
}
xq
is a wrapper aroundjq
. – Kusalananda Jan 20 '23 at 21:23