use nextjson::{
check_between, from_str, json, schema_of, to_json_schema, to_string_pretty, to_value,
validate_value, NsonDeserialize, NsonSerialize,
};
#[derive(NsonSerialize, NsonDeserialize, Clone, Debug)]
#[njson(deny_unknown_fields)]
struct Customer {
#[njson(max_str_len = 32)]
name: String,
#[njson(min = 0, max = 200)]
loyalty_points: u32,
#[njson(max_items = 8)]
tags: Vec<String>,
#[njson(sensitive)]
api_key: String,
}
#[derive(NsonSerialize, NsonDeserialize, Clone, Debug)]
#[njson(deny_unknown_fields)]
struct CustomerV2 {
#[njson(max_str_len = 32)]
name: String,
#[njson(min = 0, max = 200)]
loyalty_points: u32,
#[njson(max_items = 8)]
tags: Vec<String>,
#[njson(sensitive)]
api_key: String,
email: String,
}
fn main() -> nextjson::Result<()> {
println!("== 1. 编译期 schema ==");
println!("{:#?}", schema_of::<Customer>());
println!("\n== 2. JSON Schema 导出 ==");
let json_schema = to_json_schema::<Customer>();
println!("{}", to_string_pretty(&json_schema)?);
println!("\n== 3. 校验:合规载荷 ==");
let good: Customer = from_str(
r#"{"name":"Ada Lovelace","loyalty_points":150,"tags":["vip","analyst"],"api_key":"sk-live-abc"}"#,
)?;
let report = validate_value::<Customer>(&to_value(&good)?);
println!(
"violations = {}, is_ok = {}",
report.violations.len(),
report.is_ok()
);
println!("\n== 4. 校验:敌意载荷 ==");
let bad = json!({
"name": "a very long name exceeding the declared maximum of thirty-two scalars",
"loyalty_points": 9999,
"tags": ["t1","t2","t3","t4","t5","t6","t7","t8","t9","t10","t11","t12"],
"api_key": "sk-live-secret",
"hacker_field": "unknown",
});
let report = validate_value::<Customer>(&bad);
for violation in &report.violations {
println!(" violation @ {:?}: {:?}", violation.path, violation.kind);
}
println!(" 敏感路径(用于脱敏): {:?}", report.sensitive_paths());
println!("\n== 5. 版本兼容性:Customer -> CustomerV2 ==");
let compat = check_between::<Customer, CustomerV2>();
println!(
"forward_compatible = {} (旧 reader 能读新数据), backward_compatible = {} (新 reader 能读旧数据)",
compat.forward_compatible, compat.backward_compatible
);
for issue in &compat.issues {
println!(" [{:?}] {}: {}", issue.severity, issue.path, issue.message);
}
assert!(!compat.is_compatible(), "加必填字段必须被判定为不兼容");
Ok(())
}