Rust HJSON处理库deser-hjson的使用:支持人性化JSON格式的序列化与反序列化

Rust HJSON处理库deser-hjson的使用:支持人性化JSON格式的序列化与反序列化

这是一个为HJSON设计的Serde反序列化器,特别适合基于derive的反序列化。

HJSON是一种适合配置文件的优秀语言。这类文件应该由人类编写、阅读和修改,然后由程序反序列化为精确的结构:

let file_content = fs::read_to_string(&file_path)?;
let configuration = deser_hjson::from_str(&file_content);

如果配置文件无效或不符合预期类型,错误会详细说明期望值和错误的确切位置。

示例

use {
    deser_hjson::*,
    serde::Deserialize,
    std::collections::HashMap,
};
// 这个HJSON文档来自HJSON官网
let hjson = r#"
// 使用 #, // 或 /**/ 作为注释
// 键可以省略引号
key: 1
// 字符串可以省略引号
contains: everything on this line
// 行尾可以省略逗号
cool: {
  foo: 1
  bar: 2
}
// 允许尾随逗号
list: [
  1,
  2,
]
// 使用多行字符串
realist:
  '''
  My half empty glass,
  I will fill your empty half.
  Now you are half full.
  '''
"#;
// 我们将把它反序列化为这个结构体:
#[derive(Deserialize, PartialEq, Debug)]
struct Example {
    key: i32,
    contains: Option<String>,
    cool: HashMap<String, u16>,
    list: Vec<usize>,
    realist: String,
    missing: Option<f64>,
}
let mut cool = HashMap::new();
cool.insert("foo".to_owned(), 1);
cool.insert("bar".to_owned(), 2);
let expected = Example {
    key: 1,
    contains: Some("everything on this line".to_owned()),
    cool,
    list: vec![1, 2],
    realist: "My half empty glass,\nI will fill your empty half.\nNow you are half full.".to_owned(),
    missing: None,
};
// 这里是反序列化和相等性检查:
assert_eq!(expected, from_str(hjson).unwrap());

完整示例代码

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;

// 定义配置结构体
#[derive(Debug, Serialize, Deserialize)]
struct Config {
    server: ServerConfig,
    database: DatabaseConfig,
    logging: LoggingConfig,
}

#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
    host: String,
    port: u16,
    ssl: bool,
}

#[derive(Debug, Serialize, Deserialize)]
struct DatabaseConfig {
    url: String,
    pool_size: u32,
    timeout: f64,
}

#[derive(Debug, Serialize, Deserialize)]
struct LoggingConfig {
    level: String,
    file: Option<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 示例HJSON配置内容
    let hjson_content = r#"
// 服务器配置
server: {
    host: localhost
    port: 8080
    ssl: false
}

// 数据库配置
database: {
    url: postgres://user:pass@localhost/db
    pool_size: 10
    timeout: 30.5
}

// 日志配置
logging: {
    level: info
    // file是可选的
}
"#;

    // 反序列化HJSON到Config结构体
    let config: Config = deser_hjson::from_str(hjson_content)?;
    println!("{:#?}", config);

    // 也可以将结构体序列化为HJSON
    let serialized = deser_hjson::to_string_pretty(&config)?;
    println!("Serialized HJSON:\n{}", serialized);

    Ok(())
}

已知的开源使用案例

  • Broot可以使用TOML或HJSON进行配置(选择是基于文件扩展名动态进行的)
  • lemmy使用HJSON进行配置
  • Resc可以使用JSON或HJSON进行配置

常见问题

它适用于JSON吗?

是的,因为任何JSON文件都可以作为HJSON读取。

为什么只有基于derive的反序列化器?

在具有隐式类型的格式中猜测类型太危险了。当用户输入false时,它是字符串还是布尔值?当她输入3时,它是字符串还是数字?虽然不像YAML那样疯狂,但HJSON对此没有内部保护,因此应该只反序列化为显式类型。

为什么只有反序列化器而没有序列化器?

HJSON不是数据交换格式。它旨在由人类编写,充满注释和有意义的格式。虽然在某些上下文中序列化器是有意义的,但它们必须基于模板,或提供其他方法来指定注释和格式,而serde不是适合这种需求的工具。


1 回复

Rust HJSON处理库deser-hjson的使用指南

介绍

deser-hjson是一个Rust库,用于处理HJSON(人性化JSON)格式的数据。HJSON是JSON的一种更易读、更易写的变体,允许注释、省略引号和多行字符串等特性。

主要特性

  • 支持标准的JSON解析和序列化
  • 支持HJSON扩展语法(注释、无引号键、多行字符串等)
  • 与serde框架无缝集成
  • 人性化的错误报告

使用方法

添加依赖

在Cargo.toml中添加依赖:

[dependencies]
deser-hjson = "0.1"
serde = { version = "1.0", features = ["derive"] }

基本示例

反序列化HJSON

use serde::Deserialize;
use deser_hjson::from_str;

#[derive(Debug, Deserialize)]
struct Person {
    name: String,
    age: u8,
    hobbies: Vec<String>,
}

fn main() {
    let hjson_data = r#"
    {
        // 这是一个注释
        name: John Doe  // 键可以不加引号
        age: 30
        hobbies: [
            "reading"
            hiking  // 字符串也可以不加引号(如果没有特殊字符)
            "programming"
        ]
    }"#;

    let person: Person = from_str(hjson_data).unwrap();
    println!("{:?}", person);
}

序列化为HJSON

use serde::Serialize;
use deser_hjson::to_string_pretty;

#[derive(Debug, Serialize)]
struct Person {
    name: String,
    age: u8,
    hobbies: Vec<String>,
}

fn main() {
    let person = Person {
        name: "John Doe".to_string(),
        age: 30,
        hobbies: vec!["reading".to_string(), "hiking".to_string(), "programming".to_string()],
    };

    let hjson = to_string_pretty(&person).unwrap();
    println!("{}", hjson);
}

高级用法

自定义序列化/反序列化

use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};

#[derive(Debug, Serialize, Deserialize)]
struct Config {
    #[serde(default = "default_host")]
    host: String,
    port: u16,
    #[serde(skip_serializing_if = "Option::is_none")]
    timeout: Option<u32>,
}

fn default_host() -> String {
    "localhost".to_string()
}

fn main() {
    let config_data = r#"
    {
        // 只指定端口,使用默认主机
        port: 8080
    }"#;

    let config: Config = from_str(config_data).unwrap();
    println!("Config: {:?}", config);
    
    let hjson = to_string_pretty(&config).unwrap();
    println!("HJSON output:\n{}", hjson);
}

处理多行字符串

use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};

#[derive(Debug, Serialize, Deserialize)]
struct Article {
    title: String,
    content: String,  // 多行文本
}

fn main() {
    let article_data = r#"
    {
        title: My First Article
        content: '''
            这是文章的第一段。
            
            这是第二段,可以包含多行内容。
            甚至包含"引号"也不需要转义。
        '''
    }"#;

    let article: Article = from_str(article_data).unwrap();
    println!("Article: {:?}", article);
}

完整示例

下面是一个完整的配置管理示例,展示了如何使用deser-hjson读取和写入配置文件:

use serde::{Deserialize, Serialize};
use deser_hjson::{from_str, to_string_pretty};
use std::fs;

#[derive(Debug, Serialize, Deserialize)]
struct AppConfig {
    #[serde(default = "default_app_name")]
    app_name: String,
    debug_mode: bool,
    server: ServerConfig,
    features: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ServerConfig {
    host: String,
    port: u16,
    #[serde(default = "default_timeout")]
    timeout: u32,
}

fn default_app_name() -> String {
    "MyApp".to_string()
}

fn default_timeout() -> u32 {
    30
}

fn main() {
    // 示例HJSON配置文件
    let config_data = r#"
    {
        // 应用配置
        debug_mode: true
        server: {
            host: 127.0.0.1
            port: 8080
            // 使用默认timeout
        }
        features: [
            logging
            auth
            "data-processing"  // 包含连字符需要引号
        ]
    }"#;

    // 解析配置
    let config: AppConfig = from_str(config_data).unwrap();
    println!("Loaded config: {:#?}", config);

    // 修改配置
    let mut config = config;
    config.server.port = 9090;
    config.features.push("caching".to_string());

    // 保存配置
    let updated_config = to_string_pretty(&config).unwrap();
    fs::write("config.hjson", &updated_config).unwrap();
    println!("Saved updated config:\n{}", updated_config);
}

注意事项

  1. HJSON比标准JSON更宽松,但在与标准JSON系统交互时可能需要转换
  2. 无引号的键和字符串在包含特殊字符时需要加引号
  3. 注释在序列化时不会被保留

deser-hjson库为需要更人性化配置格式的Rust应用程序提供了很好的支持,特别适合配置文件、文档等场景。

回到顶部