jq language
The
jqlanguage queries and transforms JSON with jq 1.7 programs such as.items | map(.price) | add.
Name: jq · Crate: cameleer-core · Cargo feature: jq · Apache Camel: JQ
When to use it
- Reshape JSON in one expression: pick fields, rename them, build a new object for the next API.
- Clean up an LLM's JSON answer: parse the tool-call
argumentsstring withfromjson, apply defaults with//, keep only the fields the next step accepts. - Compute values: sums, counts, grouping, sorting.
- Filter and route with a predicate such as
.priority == "P1".
Syntax
Cameleer contains its own implementation of the jq 1.7 language (Apache Camel uses jackson-jq). Supported:
- paths
.,..,.foo,."foo",.[e],.[a:b],.[], optional? - pipes
|, comma,, alternative//, arithmetic, comparison and boolean operators - assignment
=,|=,+=,-=,*=,/=,%=,//= - array and object construction, string interpolation
"\(.x)" - formats
@text @json @html @uri @csv @tsv @sh @base64 @base64d @base32 @base32d if/elif/else/end,try/catch,reduce,foreach,label/break, variables and destructuring,?//, function definitions with recursion,$ENV,$__loc__, comments- the jq 1.7 builtins except module and I/O functions:
length keys has in map map_values path paths getpath setpath del to_entries from_entries with_entries select empty error add any all flatten range floor sqrt pow log tostring tonumber type sort sort_by group_by unique unique_by min max min_by max_by reverse contains indices index startswith endswith ltrimstr rtrimstr trim explode implode split join ascii_downcase ascii_upcase recurse transpose first last nth until while repeat limit walk tojson fromjson test match capture scan splits sub gsub todate fromdate now mktime gmtime strftime strptime abs toarray IN INDEX, math functions and more
Camel functions (as in camel-jq): header(name), header(name; default),
property(name), property(name; default), variable(name).
Result. One output gives that value, no output gives null, several outputs give an
array of all outputs. input and inputs fail: there is one input per exchange.
Input. A structured body is used as is. A text or bytes body is parsed as JSON, so a plain text body that is not JSON fails with a parse error.
Syntax reference, tested
{"body": {"store": {"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95},
{"category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99},
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99}],
"bicycle": {"color": "red", "price": 19.95}}},
"headers": {"tenant": "acme"}, "properties": {"region": "eu"}, "variables": {"user": "ada"}}| expression | result |
|---|---|
.store.book[0].title | Sayings of the Century |
.store.book[].author | ["Nigel Rees","Evelyn Waugh","Herman Melville"] |
.store.book | length | 3 |
[.store.book[] | select(.price < 10) | .title] | ["Sayings of the Century","Moby Dick"] |
.store.book | map(.price) | add | 30.93 |
{color: .store.bicycle.color, books: (.store.book | length)} | {"color":"red","books":3} |
.store.book | group_by(.category) | map({(.[0].category): length}) | add | {"fiction":2,"reference":1} |
.store.book | sort_by(.price) | .[0].title | Sayings of the Century |
[.store.book[].category] | unique | ["fiction","reference"] |
.store.bicycle | keys | ["color","price"] |
.store.bicycle | to_entries | map("\(.key)=\(.value)") | join("&") | color=red&price=19.95 |
.store.book[0].isbn // "n/a" | n/a |
if .store.bicycle.price > 15 then "pricey" else "cheap" end | pricey |
[.store.book[].title | ascii_upcase | .[0:4]] | ["SAYI","SWOR","MOBY"] |
reduce .store.book[] as $b (0; . + $b.price) | floor | 30 |
.store.book[1].title | test("honou?r"; "i") | true |
.store.book[2].author | sub("Herman "; "H. ") | H. Melville |
.store.bicycle | del(.price) | {"color":"red"} |
.store.bicycle.price |= . * 2 | .store.bicycle | {"color":"red","price":39.9} |
.store.bicycle | tojson | {"color":"red","price":19.95} |
.store.book[0] | [.author, .price] | @csv | "Nigel Rees",8.95 |
[paths(type == "number")] | length | 4 |
.store.bicycle.color, .store.bicycle.price | ["red",19.95] |
.store.missing | (empty) |
header("tenant") | acme |
header("missing"; "none") | none |
property("region") | eu |
variable("user") | ada |
As predicates, the expression is true when any output is truthy (not false or null):
| expression | result |
|---|---|
.store.bicycle.price > 15 | true |
any(.store.book[]; .price > 20) | false |
.store.book[2] | has("isbn") | true |
.store.missing | false |
Examples
Parse tool-call arguments from an LLM answer
The arguments field of an OpenAI-style tool call is a JSON string; fromjson parses it.
- route:
id: jq-tool-call
from:
uri: direct:jq-tool-call
steps:
- setHeader:
name: tool
jq: ".choices[0].message.tool_calls[0].function.name"
- setBody:
jq: '.choices[0].message.tool_calls[0].function.arguments | fromjson | {city, units: (.units // "metric")}'{"choices":[{"message":{"tool_calls":[{"id":"c1","type":"function","function":{"name":"get_weather","arguments":"{\"city\":\"Paris\"}"}}]}}]}{"city":"Paris","units":"metric"}Reshape an order and route on the result
- route:
id: jq-reshape
from:
uri: direct:jq-reshape
steps:
- setBody:
jq: '{id: .orderId, total: (.lines | map(.qty * .price) | add), customer: .customer.name}'
- choice:
when:
- jq: ".total > 100"
steps:
- setHeader:
name: lane
constant: review
otherwise:
steps:
- setHeader:
name: lane
constant: auto
- setBody:
simple: "${header.lane}: ${body}"{"orderId":"A-7","customer":{"name":"Ada"},"lines":[{"sku":"x","qty":2,"price":30},{"sku":"y","qty":1,"price":50}]}review: {"id":"A-7","total":110,"customer":"Ada"}Options
| option | default | description |
|---|---|---|
resultType | (none) | Convert the result, e.g. String, Integer, Boolean. |
headerName | (none) | Evaluate the header with this name instead of the body. |
propertyName | (none) | Evaluate this exchange property. |
variableName | (none) | Evaluate this variable. |
source | (none) | header:name, property:name, variable:name or body. |
Rust DSL
b.from("direct:jq-rust")
.filter(jq(".total > 100"))
.set_body(jq("{id, total}"));Common questions
How do I parse a JSON string that sits inside JSON?
Use fromjson, for example .function.arguments | fromjson. The reverse is tojson.
How do I give a default when a field is missing?
Use the alternative operator: .units // "metric".
Can jq read headers?
Yes: header("name"), or header("name"; "default"). Use property(...) and variable(...)
for exchange properties and variables.
Is it real jq?
It is an independent implementation of the jq 1.7 language in Rust, with no jq binary
involved. Module (import, include) and I/O functions are not available.
Related
- JSONPath language: simpler path queries
- Simple language and its
jq(...)function - JSON data format, json-validator component
- Apache Camel JQ language
Found a problem?
If something on this page doesn't match what Cameleer does, or you hit a bug while building
with it, please help fix it, whether you are a person or a coding agent:
open an issue that describes what you saw,
then fork the repository, fix it, add or update a test, run cargo test --workspace, and open
a pull request. CONTRIBUTING.md
explains the ground rules.