Rust JSON查询与处理库jaq-json的使用,高效解析和操作JSON数据的轻量级工具

Rust JSON查询与处理库jaq-json的使用,高效解析和操作JSON数据的轻量级工具

jaq (发音类似"Jacques")是一个JSON数据处理工具,是jq的克隆版本。jaq旨在支持jq的大部分语法和操作。

主要特点

jaq专注于三个目标:

  1. 正确性:提供更正确和可预测的实现,同时保持与jq的兼容性
  2. 性能:相比jq有更快的启动时间和处理速度
  3. 简洁性:实现简单小巧,减少潜在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),
            }
        }
    }
}

这个示例程序会:

  1. 创建一个包含用户数据的JSON对象
  2. 使用jaq查询筛选年龄大于30的用户
  3. 为每个匹配的用户创建一个新对象,包含姓名和是否成人的标志
  4. 输出结果

输出将是:

{"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的区别

  1. 数字处理:jaq区分整数和浮点数,避免舍入错误
  2. 赋值操作:jaq立即应用过滤器,不显式构造路径
  3. 折叠操作:jaq更自然地处理多输出过滤器
  4. 空值处理: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),
            }
        }
    }
}

这个更完整的示例展示了:

  1. 更复杂的嵌套JSON数据结构
  2. 多条件的查询筛选(部门名称、技能要求、薪资范围)
  3. 结果格式化输出
  4. 错误处理机制
  5. 选择特定字段构造新对象

预期输出:

{
  "id": 101,
  "name": "Alice",
  "salary": 85000,
  "skills": [
    "Rust",
    "Python"
  ]
}
{
  "id": 103,
  "name": "Charlie",
  "salary": 92000,
  "skills": [
    "Rust",
    "JavaScript"
  ]
}

1 回复

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

性能提示

  1. 对于大型JSON数据,考虑使用流式解析
  2. 重复使用的查询可以预编译
  3. 对于只读操作,使用不可变引用

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

这个完整示例展示了:

  1. JSON字符串的解析
  2. 基本查询操作
  3. 复杂查询和过滤
  4. JSON数据的修改
  5. 新JSON对象的创建

每个操作都有清晰的注释说明,可以直接运行测试jaq-json的各种功能。

回到顶部