A different tool, also called yq, uses a jq-like syntax and has better support for translating between XML and JSON. You can download the latest release from the Releases page. Unlike kislyuk's yq/xq it does not actually use jq for its processing, so you may have to adjust your jq scripts somewhat.
With mikefarah's yq, one can use the following command to regenerate the XML:
./yq_linux_amd64 \
--xml-attribute-prefix @ \
--xml-content-name '#text' \
--input-format yaml \
--output-format xml \
security-settings.yaml
The very same command works for the JSON inputs as well, since JSON is a subset of YAML.
Arch Linux: Install the package go-yq to install the yq executable.
I'm looking for something similar to jq but for processing XML & other similar formats. I know about yq, but I'd prefer something for XML that isn't a wrapper or changes the output to be JSON.
A different tool, also called yq, uses a jq-like syntax and has better support for translating between XML and JSON. You can download the latest release from the Releases page. Unlike kislyuk's yq/xq it does not actually use jq for its processing, so you may have to adjust your jq scripts somewhat.
With mikefarah's yq, one can use the following command to regenerate the XML:
./yq_linux_amd64 \
--xml-attribute-prefix @ \
--xml-content-name '#text' \
--input-format yaml \
--output-format xml \
security-settings.yaml
The very same command works for the JSON inputs as well, since JSON is a subset of YAML.
Arch Linux: Install the package go-yq to install the yq executable.
jq:
"@" as $attr_prefix |
"#text" as $content_key |
# ">" (only) needs to be escaped if preceded by "]]". We'll do it unconditionally.
# Some whitespace also needs escaping, at least in attribute. Not done here.
{ "&": "&", "<": "<", ">": ">" } as $escapes |
{ "&": "&", "<": "<", "\"": """ } as $attr_escapes |
def text_to_xml: split( "" ) | map( $escapes[.] // . ) | join( "" );
def text_to_xml_attr_val: split( "" ) | map( $attr_escapes[.] // . ) | join( "" );
def node_to_xml:
if type == "string" then
text_to_xml
else
(
if .attrs then
.attrs |
to_entries |
map( " " + .key + "=\"" + ( .value | text_to_xml_attr_val ) + "\"" ) |
join( "" )
else
""
end
) as $attrs |
if .children and ( .children | length ) > 0 then
( .children | map( node_to_xml ) | join( "" ) ) as $children |
"<" + .name + $attrs + ">" + $children + "</" + .name + ">"
else
"<" + .name + $attrs + "/>"
end
end
;
def fix_tree( $name ):
type as $type |
if $type == "array" then
.[] | fix_tree( $name )
elif $type == "object" then
reduce to_entries[] as { key: $k, value: $v } (
{ name: $name, attrs: {}, children: [] };
if
attr_prefix then
.attrs[
v
elif
content_key then
.children += [ $v ]
else
.children += [ $v | fix_tree( $k ) ]
end
)
else
{ name: $name, attrs: {}, children: [ . ] }
end
;
def fix_tree: fix_tree( "" ) | .children[];
fix_tree | node_to_xml
Demo on jqplay
It's invoked using
jq -r 'above progam' file.json >file.xml
You can also place the program in a file (say json_to_xml.jq) and use the following:
jq -rf json_to_xml.jq file.json >file.xml
I took a two-step approach. I first convert the input to an unambiguous format, then converting this result to XML. These two steps could be merged. Here's is the result of the first conversion of the provided input:
{
"name": "security-settings",
"attrs": {
"xmlns": "urn:activemq:core"
},
"children": [
{
"name": "security-setting",
"attrs": {
"match": "#"
},
"children": [
{
"name": "permission",
"attrs": {
"type": "createNonDurableQueue",
"roles": "admins"
},
"children": []
},
{
"name": "permission",
"attrs": {
"type": "deleteNonDurableQueue",
"roles": "admins"
},
"children": []
},
{
"name": "permission",
"attrs": {
"type": "manage",
"roles": "admins"
},
"children": []
}
]
}
]
}
Note that the format into which the original XML was converted is lossy. For example, it loses the relative order of XML elements with different names. This means the output of the above program may differ from the original XML in significant ways. But there's no escaping that unless you use a JSON schema that's not lossy.