6 个版本

0.9.5 2024年7月19日
0.9.4 2024年7月19日

#280 in 编码

Download history 437/week @ 2024-07-16 62/week @ 2024-07-23

每月499次下载

Apache-2.0

27KB
430

json-template

json-template 是一个允许您创建 JSON 模板的库。

file.json

{
   "personal_info": "{file:additional.json}",
   "info": "{personal_info.name} {personal_info.last_name} is {personal_info.age} years old"
}

additional.json

{
   "name": "Danilo",
   "last_name": "Guanabara",
   "age": 36
}

可以被渲染为

{
   "personal_info": {
      "age": 36,
      "name": "Danilo",
      "last_name": "Guanabara"
   },
   "info": "Danilo Guanabara is 36 years old"
}

功能

链式解析器

{
   "my": "Danilo",
   "name": "{my}",
   "is": "{name}
}

每个路径段都是一个占位符

{
   "data": "{{file:data.json}.property}
}

函数

内置函数

函数 描述
{file:path} 从相对路径加载一个文件。如果您反序列化一个文件,它的基本目录会自动设置。您也可以使用 Context::set_directory 手动设置。
{string:path} serde_json::Value 转换为 serde_json::Value::String。如果您需要将数字反序列化为字符串,这很有用。
{compose:{a}, {b}, ...} 将 N 个对象组合在一起。如果属性不存在,则会添加。如果属性是数组,则两个数组将被连接。输入是占位符。

查看 自定义函数 代码示例以了解如何创建自定义函数。

代码示例

您始终可以检查 测试 :)

从内存中

use json_template::*;
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Data {
   name: String,
   age: usize,
   age_str: String,
   info: String,
   time: String
}

let json = r#"{
  "data": {
    "name": "Danilo",
    "age": 36
  },
  "name": "{data.name}",
  "age": "{data.age}",
  "age_str": "{string:data.age}",
  "info": "{data.name} is {data.age} years old.",
  "time": "{data.time}"
}"#;
let context = Context::new()
   .with_data(serde_json::json!({
      "data": {
         "time": "now"
      }
   }));
let data: Data = Deserializer::new().deserialize_with_context(json, &context).unwrap();
assert_eq!(data, Data {
   name: "Danilo".into(),
   age: 36,
   age_str: "36".into(),
   info: "Danilo is 36 years old.".into(),
   time: "now".into()
})

请注意,age_str 字段是一个字符串,而 age 字段是一个数字。

简单的 "{age}" 占位符被替换为 JSON 对象中的相应值。

"{string:age}" 占位符被替换为 JSON 对象中的相应值作为字符串。

格式化占位符,如 "{data.name} is {data.age} years old." 被替换为 JSON 对象中的相应值作为字符串。

尽管 "{string:data.name} is {string:data.age} years old." 也可以工作,但这不是必需的。

从文件中

use json_template::*;
use serde::{Serialize, Deserialize};
use std::path::PathBuf;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Data {
   name: String,
   age: usize,
   age_str: String,
   info: String,
   time: String
}

let json = r#"{
  "data": "{file:tests/data.json}",
  "name": "{data.name}",
  "age": "{data.age}",
  "age_str": "{string:data.age}",
  "info": "{data.name} is {data.age} years old.",
  "time": "{data.time}"
}"#;

let directory = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let context = Context::new()
   .with_data(serde_json::json!({
      "data": {
         "time": "now"
      }
   }))
   .with_directory(Some(directory));

let data: Data = Deserializer::new().deserialize_with_context(json, &context).unwrap();

assert_eq!(data, Data {
   name: "Danilo".into(),
   age: 36,
   age_str: "36".into(),
   info: "Danilo is 36 years old.".into(),
   time: "now".into()
})

或简单地

use json_template::*;
use serde::{Serialize, Deserialize};
use std::path::PathBuf;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Data {
   name: String,
   age: usize,
   age_str: String,
   info: String,
   time: String
}

let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
    .join("tests")
    .join("data-from-file.json");
let context = Context::new()
   .with_data(serde_json::json!({
      "data": {
         "time": "now"
      }
   }));

let data: Data = Deserializer::new().deserialize_with_context(file, &context).unwrap();

assert_eq!(data, Data {
   name: "Danilo".into(),
   age: 36,
   age_str: "36".into(),
   info: "Danilo is 36 years old.".into(),
   time: "now".into()
})

自定义函数

use json_template::*;
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Time {
   duration: std::time::Duration
}

let value = serde_json::json!({ "duration": "{time:5}" });

let context = Context::new().with_function("time", |_deserializer, _context, placeholder| {
   let seconds = placeholder
      .path()
      .str()
      .parse::<u64>()
      .map_err(|e| serde::de::Error::custom(e))?;
   let duration = std::time::Duration::from_secs(seconds);
   serde_json::to_value(&duration)
});

let data: Time = Deserializer::new().deserialize_with_context(value, &context).expect("Failed to deserialize");

assert_eq!(data.duration, std::time::Duration::from_secs(5));

依赖

~0.7–1.6MB
~35K SLoC