mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
JSON reading
This commit is contained in:
parent
ddd3b6a82b
commit
fffb55f79a
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1061,6 +1061,7 @@ dependencies = [
|
|||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"same-file",
|
"same-file",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
"subsetter",
|
"subsetter",
|
||||||
"svg2pdf",
|
"svg2pdf",
|
||||||
|
@ -41,6 +41,7 @@ syntect = { version = "5", default-features = false, features = ["default-syntax
|
|||||||
rex = { git = "https://github.com/laurmaedje/ReX" }
|
rex = { git = "https://github.com/laurmaedje/ReX" }
|
||||||
lipsum = { git = "https://github.com/reknih/lipsum" }
|
lipsum = { git = "https://github.com/reknih/lipsum" }
|
||||||
csv = "1"
|
csv = "1"
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
# PDF export
|
# PDF export
|
||||||
miniz_oxide = "0.5"
|
miniz_oxide = "0.5"
|
||||||
|
@ -97,6 +97,7 @@ pub fn new() -> Scope {
|
|||||||
std.def_fn("symbol", utility::symbol);
|
std.def_fn("symbol", utility::symbol);
|
||||||
std.def_fn("lorem", utility::lorem);
|
std.def_fn("lorem", utility::lorem);
|
||||||
std.def_fn("csv", utility::csv);
|
std.def_fn("csv", utility::csv);
|
||||||
|
std.def_fn("json", utility::json);
|
||||||
|
|
||||||
// Predefined colors.
|
// Predefined colors.
|
||||||
std.define("black", Color::BLACK);
|
std.define("black", Color::BLACK);
|
||||||
|
@ -41,3 +41,46 @@ fn format_csv_error(error: csv::Error) -> String {
|
|||||||
_ => "failed to parse csv file".into(),
|
_ => "failed to parse csv file".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read structured data from a JSON file.
|
||||||
|
pub fn json(vm: &mut Vm, args: &mut Args) -> SourceResult<Value> {
|
||||||
|
let Spanned { v: path, span } =
|
||||||
|
args.expect::<Spanned<EcoString>>("path to json file")?;
|
||||||
|
|
||||||
|
let path = vm.locate(&path).at(span)?;
|
||||||
|
let data = vm.world.file(&path).at(span)?;
|
||||||
|
let value: serde_json::Value =
|
||||||
|
serde_json::from_slice(&data).map_err(format_json_error).at(span)?;
|
||||||
|
|
||||||
|
Ok(convert_json(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a JSON value to a Typst value.
|
||||||
|
fn convert_json(value: serde_json::Value) -> Value {
|
||||||
|
match value {
|
||||||
|
serde_json::Value::Null => Value::None,
|
||||||
|
serde_json::Value::Bool(v) => Value::Bool(v),
|
||||||
|
serde_json::Value::Number(v) => match v.as_i64() {
|
||||||
|
Some(int) => Value::Int(int),
|
||||||
|
None => Value::Float(v.as_f64().unwrap_or(f64::NAN)),
|
||||||
|
},
|
||||||
|
serde_json::Value::String(v) => Value::Str(v.into()),
|
||||||
|
serde_json::Value::Array(v) => {
|
||||||
|
Value::Array(v.into_iter().map(convert_json).collect())
|
||||||
|
}
|
||||||
|
serde_json::Value::Object(v) => Value::Dict(
|
||||||
|
v.into_iter()
|
||||||
|
.map(|(key, value)| (key.into(), convert_json(value)))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format the user-facing JSON error message.
|
||||||
|
fn format_json_error(error: serde_json::Error) -> String {
|
||||||
|
assert!(error.is_syntax() || error.is_eof());
|
||||||
|
format!(
|
||||||
|
"failed to parse json file: syntax error in line {}",
|
||||||
|
error.line()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
4
tests/res/bad.json
Normal file
4
tests/res/bad.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"invalid": True
|
||||||
|
}
|
20
tests/res/zoo.json
Normal file
20
tests/res/zoo.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Debby",
|
||||||
|
"species": "Rhinoceros",
|
||||||
|
"weight": 1900,
|
||||||
|
"length": 390
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Fluffy",
|
||||||
|
"species": "Tiger",
|
||||||
|
"weight": 115,
|
||||||
|
"length": 310
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sleepy",
|
||||||
|
"species": "Dolphin",
|
||||||
|
"weight": 150,
|
||||||
|
"length": 180
|
||||||
|
}
|
||||||
|
]
|
@ -1,6 +1,9 @@
|
|||||||
// Test reading structured CSV data.
|
// Test reading structured data.
|
||||||
|
// Ref: false
|
||||||
|
|
||||||
---
|
---
|
||||||
|
// Test reading CSV data.
|
||||||
|
// Ref: true
|
||||||
#set page(width: auto)
|
#set page(width: auto)
|
||||||
#let data = csv("/res/zoo.csv")
|
#let data = csv("/res/zoo.csv")
|
||||||
#let cells = data(0).map(strong) + data.slice(1).flatten()
|
#let cells = data(0).map(strong) + data.slice(1).flatten()
|
||||||
@ -13,3 +16,14 @@
|
|||||||
---
|
---
|
||||||
// Error: 6-20 failed to parse csv file: found 3 instead of 2 fields in line 3
|
// Error: 6-20 failed to parse csv file: found 3 instead of 2 fields in line 3
|
||||||
#csv("/res/bad.csv")
|
#csv("/res/bad.csv")
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test reading JSON data.
|
||||||
|
#let data = json("/res/zoo.json")
|
||||||
|
#test(data.len(), 3)
|
||||||
|
#test(data(0).name, "Debby")
|
||||||
|
#test(data(2).weight, 150)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 7-22 failed to parse json file: syntax error in line 3
|
||||||
|
#json("/res/bad.json")
|
Loading…
x
Reference in New Issue
Block a user