Rust JSON查询与处理库jaq-json的使用,高效解析和操作JSON数据的轻量级工具
Rust JSON查询与处理库jaq-json的使用,高效解析和操作JSON数据的轻量级工具
jaq (发音类似"Jacques")是一个JSON数据处理工具,是jq的克隆版本。jaq旨在支持jq的大部分语法和操作。
主要特点
jaq专注于三个目标:
- 正确性:提供更正确和可预测的实现,同时保持与jq的兼容性
- 性能:相比jq有更快的启动时间和处理速度
- 简洁性:实现简单小巧,减少潜在bug并便于贡献
安装方式
二进制安装
Linux系统可通过以下命令安装:
$ curl -fsSL https://github.com/01mf02/jaq/releases/latest/download/jaq-$(uname -m)-unknown-linux-musl -o jaq && chmod +x jaq
macOS或Linux也可使用homebrew安装:
$ brew install jaq
$ brew install --HEAD jaq # 安装最新开发版本
源码编译
需要先安装Rust工具链,然后执行:
$ cargo install --locked jaq
$ cargo install --locked --git https://github.com/01mf02/jaq # 最新开发版本
使用示例
基本查询
访问字段:
$ echo '{"a": 1, "b": 2}' | jaq '.a'
1
数值相加:
$ echo '{"a": 1, "b": 2}' | jaq 'add'
3
数组操作
从对象构造数组并比较:
$ echo '{"a": 1, "b": 2}' | jaq '[.a, .b] == [.[]]'
true
对数组元素应用过滤器并筛选结果:
$ echo '[0, 1, 2, 3]' | jaq 'map(.*2) | [.[] | select(. < 5)]'
[0, 2, 4]
高级功能
读取输入值到数组并计算平均值:
$ echo '1 2 3 4' | jaq -s 'add / length'
2.5
递归应用过滤器并输出中间结果:
$ echo '0' | jaq '[recurse(.+1; . < 3)]'
[0, 1, 2]
懒加载折叠输入并输出中间结果:
$ seq 1000 | jaq -n 'foreach inputs as $x (0; . + $x)'
1 3 6 10 15 [...]
完整示例代码
下面是一个完整的Rust程序示例,展示如何使用jaq-json库处理JSON数据:
use jaq_interpret::{parse, Ctx, Filter, RcIter, Val};
use serde_json::{json, Value};
fn main() {
// 定义输入JSON数据
let input = json!({
"users": [
{"id": 1, "name": "Alice", "age": 30},
{"id": 2, "name": "Bob", "age": 25},
{"id": 3, "name": "Charlie", "age": 35}
]
});
// 定义jaq查询
let query = r#"
.users[]
| select(.age > 30)
| {name: .name, isAdult: (.age >= 18)}
"#;
// 解析查询
let (filters, errs) = parse(query, jaq_parse::main());
assert!(errs.is_empty(), "Parse errors: {:?}", errs);
// 准备上下文
let ctx = Ctx::new([], RcIter::new(core::iter::empty()));
let inputs = RcIter::new(core::iter::once(Val::from(input)));
// 执行查询
for filter in filters {
for result in filter.run((ctx.clone(), inputs.clone())) {
match result {
Ok(val) => {
// 将结果转换回serde_json::Value
let value: Value = val.into();
println!("{}", value);
}
Err(e) => eprintln!("Error: {}", e),
}
}
}
}
这个示例程序会:
- 创建一个包含用户数据的JSON对象
- 使用jaq查询筛选年龄大于30的用户
- 为每个匹配的用户创建一个新对象,包含姓名和是否成人的标志
- 输出结果
输出将是:
{"isAdult":true,"name":"Charlie"}
性能对比
jaq在大多数基准测试中表现优于jq和gojq。例如:
Benchmark | n | jaq-2.3 | jq-1.8.1 | gojq-0.12.17 |
---|---|---|---|---|
empty |
512 | 330 | 440 | 290 |
bf-fib |
13 | 430 | 1110 | 540 |
reverse |
1048576 | 30 | 500 | 270 |
sort |
1048576 | 100 | 450 | 540 |
安全特性
jaq核心已经通过Radically Open Security的安全审计,发现了一个低严重性问题和三个可能无法利用的问题。jaq还包含:
- 多个模糊测试目标
- 超过500个测试用例的测试套件
- 精心设计的JSON解析器
主要功能
jaq支持大多数jq功能,包括:
- 基本数据类型和操作
- 路径索引和迭代
- 各种运算符
- 函数定义
- 核心过滤器(empty, error, inputs等)
- 标准过滤器(map, select, join等)
- 数值运算和数学函数
- 模块系统
与jq的区别
- 数字处理:jaq区分整数和浮点数,避免舍入错误
- 赋值操作:jaq立即应用过滤器,不显式构造路径
- 折叠操作:jaq更自然地处理多输出过滤器
- 空值处理:jaq对null值的索引会报错,防止意外行为
jaq是一个功能强大且高效的JSON处理工具,特别适合需要高性能JSON处理的Rust应用程序。它的语法与jq高度兼容,使得熟悉jq的用户可以轻松上手。
完整示例demo
基于上述内容,这里提供一个更完整的jaq-json使用示例:
use jaq_interpret::{parse, Ctx, Filter, RcIter, Val};
use serde_json::{json, Value};
fn main() {
// 更复杂的JSON数据结构
let input = json!({
"company": "TechCorp",
"departments": [
{
"name": "Engineering",
"employees": [
{"id": 101, "name": "Alice", "skills": ["Rust", "Python"], "salary": 85000},
{"id": 102, "name": "Bob", "skills": ["Java", "Go"], "salary": 78000},
{"id": 103, "name": "Charlie", "skills": ["Rust", "JavaScript"], "salary": 92000}
]
},
{
"name": "Marketing",
"employees": [
{"id": 201, "name": "David", "skills": ["SEO", "Content"], "salary": 65000},
{"id": 202, "name": "Eve", "skills": ["Social", "Analytics"], "salary": 72000}
]
}
]
});
// 复杂的jaq查询:找出工程部门中会Rust且薪资高于80000的员工
let query = r#"
.departments[]
| select(.name == "Engineering")
| .employees[]
| select(any(.skills[]; . == "Rust")
| select(.salary > 80000)
| {id: .id, name: .name, salary: .salary, skills: .skills}
"#;
// 解析查询
let (filters, errs) = parse(query, jaq_parse::main());
if !errs.is_empty() {
eprintln!("查询解析错误: {:?}", errs);
return;
}
// 准备上下文和输入
let ctx = Ctx::new([], RcIter::new(core::iter::empty()));
let inputs = RcIter::new(core::iter::once(Val::from(input)));
// 执行查询并处理结果
println!("查询结果:");
for filter in filters {
for result in filter.run((ctx.clone(), inputs.clone())) {
match result {
Ok(val) => {
let value: Value = val.into();
println!("{}", serde_json::to_string_pretty(&value).unwrap());
}
Err(e) => eprintln!("查询执行错误: {}", e),
}
}
}
}
这个更完整的示例展示了:
- 更复杂的嵌套JSON数据结构
- 多条件的查询筛选(部门名称、技能要求、薪资范围)
- 结果格式化输出
- 错误处理机制
- 选择特定字段构造新对象
预期输出:
{
"id": 101,
"name": "Alice",
"salary": 85000,
"skills": [
"Rust",
"Python"
]
}
{
"id": 103,
"name": "Charlie",
"salary": 92000,
"skills": [
"Rust",
"JavaScript"
]
}
Rust JSON查询与处理库jaq-json的使用指南
jaq-json 是一个轻量级的 Rust JSON 查询和处理库,提供了高效的方式来解析和操作 JSON 数据。它特别适合需要快速查询和转换 JSON 数据的场景。
主要特性
- 轻量级且高效
- 支持类似jq的查询语法
- 零拷贝解析
- 易于使用的API
安装方法
在Cargo.toml中添加依赖:
[dependencies]
jaq-json = "0.5"
基本使用方法
1. 解析JSON字符串
use jaq_json::{parse, Val};
fn main() {
let json_str = r#"{"name": "Alice", "age": 30, "hobbies": ["reading", "hiking"]}"#;
let json_value = parse(json_str).unwrap();
println!("Parsed JSON: {:?}", json_value);
}
2. 查询JSON数据
jaq-json支持类似jq的查询语法:
use jaq_json::{parse, query};
fn main() {
let json_str = r#"{
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
}"#;
let json_value = parse(json_str).unwrap();
// 查询所有用户名
let names = query(".users[].name", &json_value).unwrap();
println!("User names: {:?}", names);
// 查询年龄大于28的用户
let adults = query(".users[] | select(.age > 28)", &json_value).unwrap();
println!("Adult users: {:?}", adults);
}
3. 修改JSON数据
use jaq_json::{parse, Val};
fn main() {
let mut json_value = parse(r#"{"name": "Alice", "age": 30}"#).unwrap();
// 修改字段值
if let Val::Object(map) = &mut json_value {
map.insert("age".to_string(), Val::Num(31.0));
}
println!("Updated JSON: {:?}", json_value);
}
4. 创建新JSON
use jaq_json::Val;
fn main() {
let mut new_json = Val::Object(Default::default());
if let Val::Object(map) = &mut new_json {
map.insert("name".to_string(), Val::Str("Charlie".to_string()));
map.insert("age".to_string(), Val::Num(28.0));
map.insert("active".to_string(), Val::Bool(true));
}
println!("New JSON: {:?}", new_json);
}
高级查询示例
复杂查询
use jaq_json::{parse, query};
fn main() {
let json_str = r#"{
"store": {
"books": [
{"title": "Rust in Action", "price": 39.99, "stock": 5},
{"title": "The Rust Programming Language", "price": 29.99, "stock": 10},
{"title": "Zero to Production in Rust", "price": 34.99, "stock": 2}
]
}
}"#;
let json_value = parse(json_str).unwrap();
// 查询库存少于5本的书籍标题和价格
let low_stock = query(".store.books[] | select(.stock < 5) | {title, price}", &json_value).unwrap();
println!("Low stock books: {:?}", low_stock);
// 计算所有书籍的总价值
let total_value = query("[.store.books[] | .price * .stock] | add", &json_value).unwrap();
println!("Total inventory value: {:?}", total_value);
}
性能提示
- 对于大型JSON数据,考虑使用流式解析
- 重复使用的查询可以预编译
- 对于只读操作,使用不可变引用
jaq-json 是一个强大而灵活的工具,特别适合需要在Rust中高效处理JSON数据的场景。它的查询语法与jq兼容,使得从其他语言迁移过来的开发者能够快速上手。
完整示例demo
下面是一个完整的示例,展示了jaq-json的主要功能:
use jaq_json::{parse, query, Val};
fn main() {
// 示例1: 解析JSON字符串
let json_str = r#"{
"company": "TechCorp",
"employees": [
{"id": 1, "name": "Alice", "department": "Engineering", "salary": 85000},
{"id": 2, "name": "Bob", "department": "Marketing", "salary": 75000},
{"id": 3, "name": "Charlie", "department": "Engineering", "salary": 90000}
],
"locations": ["New York", "San Francisco", "Austin"]
}"#;
// 解析JSON
let json_value = parse(json_str).unwrap();
println!("Parsed JSON:\n{:#?}\n", json_value);
// 示例2: 基本查询
// 查询所有员工姓名
let names = query(".employees[].name", &json_value).unwrap();
println!("All employee names: {:?}\n", names);
// 查询工程部门的员工
let engineers = query(".employees[] | select(.department == \"Engineering\")", &json_value).unwrap();
println!("Engineering department employees: {:#?}\n", engineers);
// 示例3: 复杂查询
// 查询高薪员工(工资>80000)的姓名和部门
let high_earners = query(".employees[] | select(.salary > 80000) | {name, department}", &json_value).unwrap();
println!("High earning employees: {:#?}\n", high_earners);
// 计算平均工资
let avg_salary = query("[.employees[] | .salary] | add / length", &json_value).unwrap();
println!("Average salary: {:?}\n", avg_salary);
// 示例4: 修改JSON数据
let mut json_value = json_value;
if let Val::Object(ref mut map) = json_value {
// 添加新字段
map.insert("founded_year".to_string(), Val::Num(2010.0));
// 修改现有字段
if let Some(Val::Array(ref mut employees)) = map.get_mut("employees") {
if let Some(Val::Object(ref mut first_employee)) = employees.get_mut(0) {
first_employee.insert("salary".to_string(), Val::Num(88000.0));
}
}
}
println!("Modified JSON:\n{:#?}\n", json_value);
// 示例5: 创建新JSON
let mut new_json = Val::Object(Default::default());
if let Val::Object(ref mut map) = new_json {
map.insert("product".to_string(), Val::Str("jaq-json".to_string()));
map.insert("version".to_string(), Val::Num(0.5));
map.insert("features".to_string(), Val::Array(vec![
Val::Str("jq-like syntax".to_string()),
Val::Str("zero-copy".to_string()),
Val::Str("high performance".to_string())
]));
}
println!("Newly created JSON:\n{:#?}", new_json);
}
这个完整示例展示了:
- JSON字符串的解析
- 基本查询操作
- 复杂查询和过滤
- JSON数据的修改
- 新JSON对象的创建
每个操作都有清晰的注释说明,可以直接运行测试jaq-json的各种功能。