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(())
}

此示例演示了如何:

  1. 创建 JSON 数据
  2. 定义 jaq 查询过滤器
  3. 解析和应用过滤器
  4. 处理查询结果

要运行此代码,请确保在 Cargo.toml 中添加依赖:

[dependencies]
jaq-interpret = "1.5.0"
serde_json = "1.0"

这个示例展示了 jaq-interpret 的基本用法,您可以根据需要修改查询过滤器来处理更复杂的 JSON 数据操作。


1 回复

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(())
}

性能建议

  1. 对于大型JSON文件,考虑使用流式处理
  2. 复用解析器实例以提高性能
  3. 使用适当的查询优化技巧

注意事项

  • 确保输入的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
}
回到顶部