Rust JSON查询引擎jaq-interpret的使用,高效解析与操作JSON数据的Rust插件库
jaq(发音类似 Jacques)是 JSON 数据处理工具 jq 的克隆。jaq 旨在支持 jq 语法和操作的大部分子集。
您可以在 jaq 游乐场在线尝试 jaq。
jaq 专注于三个目标:
- 正确性:jaq 旨在提供更正确和可预测的 jq 实现,同时在大多数情况下保持与 jq 的兼容性。
- 性能:我最初创建 jaq 是因为我对 jq 的长启动时间感到困扰,这在我的机器上大约需要 50 毫秒。这在处理大量小文件时尤其明显。jaq 的启动速度比 jq 1.6 快约 30 倍,并且在许多其他基准测试中也优于 jq。
- 简洁性:jaq 旨在拥有简单且小的实现,以减少潜在的错误并促进贡献。
我从另一个 Rust 程序 jql 中获得了灵感。然而,与 jql 不同,jaq 旨在紧密模仿 jq 的语法和语义。这应该使熟练使用 jq 的用户能够轻松使用 jaq。
安装
二进制文件
您可以在发布页面下载适用于 Linux、Mac 和 Windows 的二进制文件。
您也可以使用 macOS 或 Linux 上的 homebrew 安装 jaq:
$ brew install jaq
$ brew install --HEAD jaq # 最新的开发版本
或在 Windows 上使用 scoop:
$ scoop install main/jaq
从源码编译
要编译 jaq,您需要 Rust 工具链。请参阅获取说明。(请注意,Linux 发行版附带的 Rust 编译器可能太旧,无法编译 jaq。)
以下任一命令都可以安装 jaq:
$ cargo install --locked jaq
$ cargo install --locked --git # 最新的开发版本
在我的系统上,这两个命令都将可执行文件放置在 ~/.cargo/bin/jaq
。
如果您已经克隆了此存储库,您也可以通过执行克隆存储库中的以下命令之一来构建 jaq:
$ cargo build --release # 将二进制文件放入 target/release/jaq
$ cargo install --locked --path jaq # 安装二进制文件
jaq 应该可以在 Rust 支持的任何系统上工作。如果不行,请提交问题。
示例
以下示例应该让您了解 jaq 目前可以做什么。您应该通过将 jaq 替换为 jq 获得相同的输出。如果没有,您提交问题将不胜感激。:) 语法在 jq 手册中有文档记录。
访问字段:
$ 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-interpret 库来解析和查询 JSON 数据:
use jaq_interpret::{parse, Filter, Val};
use serde_json::json;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 示例 JSON 数据
let data = json!({
"name": "Alice",
"age": 30,
"hobbies": ["reading", "cycling", "hiking"]
});
// 将 JSON 数据转换为 jaq 的 Val 类型
let input = Val::from(data);
// 定义 jaq 查询过滤器
let filter_str = ".name, .age, .hobbies[]"; // 查询名称、年龄和所有爱好
// 解析过滤器
let (_, main) = parse::parse(&filter_str, parse::main()).map_err(|e| e.to_string())?;
let filter = Filter::new(main);
// 应用过滤器并处理结果
let mut results = filter.run((input, Default::default()));
for result in results {
match result {
Ok(val) => println!("{}", val),
Err(e) => eprintln!("Error: {}", e),
}
}
Ok(())
}
此示例演示了如何:
- 创建 JSON 数据
- 定义 jaq 查询过滤器
- 解析和应用过滤器
- 处理查询结果
要运行此代码,请确保在 Cargo.toml 中添加依赖:
[dependencies]
jaq-interpret = "1.5.0"
serde_json = "1.0"
这个示例展示了 jaq-interpret 的基本用法,您可以根据需要修改查询过滤器来处理更复杂的 JSON 数据操作。
jaq-interpret:Rust JSON查询引擎
介绍
jaq-interpret是一个基于Rust语言开发的高性能JSON查询引擎库。该库实现了jaq查询语言的解释器,专门用于高效解析和操作JSON数据。jaq查询语言灵感来源于jq,但专注于提供更简洁的语法和更好的性能表现。
主要特性
- 完整的jaq查询语言支持
- 零拷贝JSON解析
- 类型安全的查询操作
- 高性能的查询执行
- 丰富的内置函数支持
安装方法
在Cargo.toml中添加依赖:
[dependencies]
jaq-interpret = "0.8"
serde_json = "1.0"
基本使用方法
1. 简单查询示例
use jaq_interpret::{parse, Val};
use serde_json::Value;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json_data = r#"
{
"users": [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30}
]
}
"#;
let data: Value = serde_json::from_str(json_data)?;
let query = ".users[].name";
let parsed = parse::parse(&query, parse::main())?;
let results = parsed.run(Val::from(data));
for result in results {
println!("{}", result?);
}
Ok(())
}
2. 复杂查询示例
use jaq_interpret::{parse, Val};
use serde_json::Value;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json_data = r#"
{
"products": [
{"name": "Laptop", "price": 999.99, "stock": 15},
{"name": "Mouse", "price": 25.50, "stock": 0},
{"name": "Keyboard", "price": 75.00, "stock": 8}
]
}
"#;
let data: Value = serde_json::from_str(json_data)?;
// 查询有库存的产品名称和价格
let query = ".products[] | select(.stock > 0) | {name, price}";
let parsed = parse::parse(&query, parse::main())?;
let results = parsed.run(Val::from(data));
for result in results {
println!("{}", result?);
}
Ok(())
}
3. 使用内置函数
use jaq_interpret::{parse, Val};
use serde_json::Value;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json_data = r#"
{
"numbers": [1, 2, 3, 4, 5]
}
"#;
let data: Value = serde_json::from_str(json_data)?;
// 使用数学函数处理数据
let query = ".numbers[] | . * 2 | sqrt";
let parsed = parse::parse(&query, parse::main())?;
let results = parsed.run(Val::from(data));
for result in results {
println!("{}", result?);
}
Ok(())
}
高级功能
自定义函数
use jaq_interpret::{parse, Val, Ctx, RcIter};
use serde_json::Value;
fn custom_function(ctx: &Ctx, args: RcIter<Val>) -> Option<Val> {
// 实现自定义函数逻辑
// ...
None
}
fn main() {
// 注册和使用自定义函数
}
错误处理
use jaq_interpret::{parse, Val, Error};
use serde_json::Value;
fn process_json() -> Result<(), Error> {
let data: Value = serde_json::from_str(r#"{"data": "example"}"#).unwrap();
let query = ".data";
let parsed = parse::parse(&query, parse::main())?;
let mut results = parsed.run(Val::from(data));
match results.next() {
Some(Ok(result)) => println!("Result: {}", result),
Some(Err(e)) => return Err(e),
None => println!("No results"),
}
Ok(())
}
性能建议
- 对于大型JSON文件,考虑使用流式处理
- 复用解析器实例以提高性能
- 使用适当的查询优化技巧
注意事项
- 确保输入的JSON数据格式正确
- 查询语法遵循jaq规范
- 注意错误处理和类型安全
这个库特别适合需要高性能JSON处理的Rust应用程序,特别是在数据分析和ETL场景中表现优异。
完整示例代码
use jaq_interpret::{parse, Val, Error};
use serde_json::Value;
// 完整的数据处理示例
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 示例JSON数据
let json_data = r#"
{
"company": "TechCorp",
"employees": [
{
"id": 1,
"name": "Alice",
"department": "Engineering",
"salary": 80000,
"skills": ["Rust", "Python", "JavaScript"]
},
{
"id": 2,
"name": "Bob",
"department": "Marketing",
"salary": 65000,
"skills": ["SEO", "Content Writing"]
},
{
"id": 3,
"name": "Charlie",
"department": "Engineering",
"salary": 95000,
"skills": ["Rust", "Go", "Kubernetes"]
}
]
}
"#;
// 解析JSON数据
let data: Value = serde_json::from_str(json_data)?;
println!("=== 查询1: 获取所有员工姓名 ===");
let query1 = ".employees[].name";
execute_query(&query1, &data)?;
println!("\n=== 查询2: 获取工程部门员工信息 ===");
let query2 = ".employees[] | select(.department == \"Engineering\") | {name, salary}";
execute_query(&query2, &data)?;
println!("\n=== 查询3: 获取高薪员工(薪资>70000)===");
let query3 = ".employees[] | select(.salary > 70000) | {name, department, salary}";
execute_query(&query3, &data)?;
println!("\n=== 查询4: 使用数学函数计算薪资调整 ===");
let query4 = ".employees[] | {name, current_salary: .salary, adjusted_salary: (.salary * 1.1)}";
execute_query(&query4, &data)?;
Ok(())
}
// 执行查询的辅助函数
fn execute_query(query: &str, data: &Value) -> Result<(), Error> {
let parsed = parse::parse(query, parse::main())?;
let results = parsed.run(Val::from(data.clone()));
for result in results {
match result {
Ok(val) => println!("{}", val),
Err(e) => eprintln!("查询错误: {}", e),
}
}
Ok(())
}
// 自定义函数示例
#[allow(dead_code)]
fn calculate_bonus(ctx: &jaq_interpret::Ctx, args: jaq_interpret::RcIter<Val>) -> Option<Val> {
// 实现计算奖金的逻辑
// 这里只是一个示例,实际实现需要处理参数
None
}