Reputation: 73
I have the following JSON that I need to transform with jq. The main task is to leave only specified attributes in the tree. The number of levels in the tree may be different.
{
"metaModel": [ {
"clazz": "AttributeClass1",
"code": "code1",
"default": "value1",
"children": [ {
"clazz": "AttributeClass2",
"code": "code21",
"default": "value21"
},
{
"clazz": "AttributeClass1",
"code": "code22",
"default": "value22",
"children": [ {
"clazz": "AttributeClass1",
"code": "code31",
"default": "value31",
"children": []
}
]
}
]
},
{
"clazz": "AttributeClass2",
"code": "code2",
"default": "value2"
}
]
}
Is it possible to get the output like the following (leave only clazz, code, children)?
{
"clazz": "AttributeClass1",
"code": "code1",
"children": [ {
"clazz": "AttributeClass2",
"code": "code21"
},
{
"clazz": "AttributeClass1",
"code": "code22",
"children": [ {
"clazz": "AttributeClass1",
"code": "code31",
"children": []
}
]
}
]
},
{
"clazz": "AttributeClass2",
"code": "code2"
}
'metaModel' may be left in the output too.
Upvotes: 0
Views: 1024
Reputation: 116750
Using walk/1
:
.metaModel
| walk( if type == "object" and has("clazz")
then {clazz, code} + (if .children then { children } else null end )
else .
end )
Upvotes: 1
Reputation: 14665
Here is an approach that uses tostream, setpath and reduce:
reduce (tostream|select(.[0][-1]|.=="clazz" or .=="code" or .=="children")) as [$p,$v] (
{}; setpath($p;$v)
)
tostream converts your input to "streaming" [path,value] form:
[["metaModel",0,"children",0,"clazz"],"AttributeClass2"]
[["metaModel",0,"children",0,"code"],"code21"]
[["metaModel",0,"children",0,"default"],"value21"]
[["metaModel",0,"children",0,"default"]]
[["metaModel",0,"children",1,"children",0,"children"],[]]
[["metaModel",0,"children",1,"children",0,"clazz"],"AttributeClass1"]
[["metaModel",0,"children",1,"children",0,"code"],"code31"]
[["metaModel",0,"children",1,"children",0,"default"],"value31"]
[["metaModel",0,"children",1,"children",0,"default"]]
[["metaModel",0,"children",1,"children",0]]
[["metaModel",0,"children",1,"clazz"],"AttributeClass1"]
[["metaModel",0,"children",1,"code"],"code22"]
[["metaModel",0,"children",1,"default"],"value22"]
[["metaModel",0,"children",1,"default"]]
[["metaModel",0,"children",1]]
[["metaModel",0,"clazz"],"AttributeClass1"]
[["metaModel",0,"code"],"code1"]
[["metaModel",0,"default"],"value1"]
[["metaModel",0,"default"]]
[["metaModel",1,"clazz"],"AttributeClass2"]
[["metaModel",1,"code"],"code2"]
[["metaModel",1,"default"],"value2"]
[["metaModel",1,"default"]]
[["metaModel",1]]
[["metaModel"]]
select filters out all paths not ending in "clazz", "code" or "children":
[["metaModel",0,"children",0,"clazz"],"AttributeClass2"]
[["metaModel",0,"children",0,"code"],"code21"]
[["metaModel",0,"children",1,"children",0,"children"],[]]
[["metaModel",0,"children",1,"children",0,"clazz"],"AttributeClass1"]
[["metaModel",0,"children",1,"children",0,"code"],"code31"]
[["metaModel",0,"children",1,"clazz"],"AttributeClass1"]
[["metaModel",0,"children",1,"code"],"code22"]
[["metaModel",0,"clazz"],"AttributeClass1"]
[["metaModel",0,"code"],"code1"]
[["metaModel",1,"clazz"],"AttributeClass2"]
[["metaModel",1,"code"],"code2"]
reduce and setpath reconstruct the final result:
{
"metaModel": [
{
"children": [
{
"clazz": "AttributeClass2",
"code": "code21"
},
{
"children": [
{
"children": [],
"clazz": "AttributeClass1",
"code": "code31"
}
],
"clazz": "AttributeClass1",
"code": "code22"
}
],
"clazz": "AttributeClass1",
"code": "code1"
},
{
"clazz": "AttributeClass2",
"code": "code2"
}
]
}
Upvotes: 0
Reputation: 116750
The following recursive function seems to meet the requirements, or at least the filter .metaModel | transform
transforms the sample input in accordance with the sample output:
def transform:
if type == "object" and has("clazz")
then {clazz, code} + (if has("children") then {children: (.children|transform)} else null end)
elif type == "array" then map(transform)
else .
end;
If you don't like the redundancy of the expression
{children: (.children|transform)}
you could write:
{children} | map_values(transform)
Upvotes: 1