silk.json.value.
Parse JSON into a tree of values, read fields from it, and write it back.
When to use#
Use Json.parse when the shape of a document is not fixed, or when you need only a few fields
of a larger document. Read the fields with Json.field, Json.fieldAt, and the as...
functions. Use Json.deserialize or Json.decode to read a document straight into a typed
value, and Json.serialize to write a typed value. To read tokens without building a tree,
use silk.json.scanner.
Details#
Parsing is strict (RFC 8259): no comments, no trailing commas, and no duplicate keys in an
object. Arrays and objects keep the order of the source. A number keeps its source text and is
converted only when you call Json.asI32 or a similar function. String escapes are decoded
when the document is parsed. If parsing fails, everything that was built is released.
Errors#
JsonErrorif the input is not valid JSON, has a duplicate key, nests deeper thanMAX_DEPTH(64), or has content after the value.offsetgives the position.OutOfMemoryErrorif theAllocatorcannot supply the tree.WriterErrorfrom the write functions if theWriterfails.
Gotchas#
A borrowed field or string cannot live longer than the Value that owns it. The write
functions do not flush the Writer.
Examples#
Read fields from a document#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.json.scanner { JsonError }
import silk.json.value { Json }
import silk.core.option { Option }
pub effect fn main() -> i32
! JsonError | OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let document = run Json.parse(b"{\"id\":42,\"user\":{\"name\":\"Ada\"}}")
|> Effect.provideMut<Allocator>(&mut allocator)
let id = Json.field(&document, "id") |> Json.asI32 |> Option<i32>.unwrapOr(0)
let name = Json.field(&document, "user") |> Json.fieldAt("name") |> Json.asString
|> Option<string>.unwrapOr("")
if id != 42 || name != "Ada" {
return 1
}
return 0
}Read a typed value directly#
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.json.scanner { JsonError }
import silk.json.value { Json }
import silk.collections.list { List }
pub effect fn main() -> i32
! JsonError | OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let values = run Json.deserialize<List<i64>>(b"[1, 2, 39]")
|> Effect.provideMut<Allocator>(&mut allocator)
if List<i64>.length(&values) != 3 {
return 1
}
return 0
}Import as Json with import silk.json.value { Json }.
Public declarations: 4.
JsonEntry#
pub struct JsonEntryOne key and value of a JSON object.
Field key#
pub key: StringThe key, with escapes decoded.
Field value#
pub value: ValueThe value.
JsonChild#
pub union JsonChildOne element of an array or one member of an object.
Details#
Arrays and objects use the same storage type, so nested arrays and objects drop in the same way.
Item#
ItemAn array element.
Field value#
pub value: ValueThe element.
Member#
MemberAn object member.
Field entry#
pub entry: JsonEntryThe key and the value.
Value#
pub union ValueOne JSON value: null, a boolean, a number, a string, an array, or an object.
Details#
A Value owns all of its children. Use match to get the kind, or the Json.as... functions
to read a field of a known kind. Arrays and objects keep the order of the source. A number keeps
its source text and is converted only when you call Json.asI32 or a similar function.
Gotchas#
A borrowed field or string cannot live longer than the Value that owns it.
Null#
NullJSON null.
Bool#
BoolJSON true or false.
Field value#
pub value: boolThe boolean value.
Number#
NumberA JSON number, kept as its source text.
Field text#
pub text: StringThe number as it was written in the source, such as "-1.5e3".
String#
StringA JSON string.
Field text#
pub text: StringThe text, with escapes decoded.
Array#
ArrayA JSON array.
Field values#
pub values: Box<List<JsonChild>>The elements in source order. Each is a JsonChild.Item.
Object#
ObjectA JSON object.
Field entries#
pub entries: Box<List<JsonChild>>The members in source order. Each is a JsonChild.Member.
Json#
pub struct JsonThe owner type of the JSON parse, write, and read operations. It has no fields and is never constructed.
Implementation Json#
impl JsonAssociated function parse#
pub effect fn parse(bytes: &[u8]) -> Value ! JsonError | OutOfMemoryError ? &mut AllocatorParses bytes as one complete JSON document and returns its tree.
When to use#
Use it when the shape of a document is not fixed, or when you need only a few fields of a
larger document. To read a typed value, use Json.deserialize or Json.decode.
Details#
Parsing is strict (RFC 8259): no comments, no trailing commas, and no duplicate keys in an object. String escapes are decoded when the document is parsed.
Errors#
JsonErrorifbytesis not one valid JSON value, if an object has a duplicate key, if the nesting is deeper than 64, or if content follows the value. The partial tree is dropped.OutOfMemoryErrorif theAllocatorcannot supply the tree.
Associated function decode#
pub fn decode<T: Decode>(bytes: &[u8]) -> Result<T, JsonError>Reads bytes as one complete JSON document of type T. It does not allocate.
When to use#
Use it for types that implement Decode, such as numbers and bool. Use Json.deserialize
for types that need memory, such as String and List<T>.
Errors#
JsonErrorif the JSON is not valid, the value does not have typeT(TypeMismatch), a number does not fit (NumberOutOfRange), or content follows the value (TrailingContent).
Associated function deserialize#
pub effect fn deserialize<T: Deserialize>(bytes: &[u8])
-> T ! JsonError | OutOfMemoryError ? &mut AllocatorReads bytes as one complete JSON document of type T.
Errors#
JsonErrorif the JSON is not valid, the value does not have typeT, or content follows the value.OutOfMemoryErrorif theAllocatorcannot supply the value.
Associated function serialize#
pub effect fn serialize<T: Serialize>(value: &T, options: &JsonOptions)
-> () ! JsonError | WriterError ? &mut WriterWrites value as JSON to the Writer, with options.
Errors#
JsonErrorif the nesting is deeper than the limit inoptions, or a float is not finite.WriterErrorif theWriterfails. Bytes that it accepted before the failure stay written.
Examples#
Write a struct#
import silk.core.effect { Effect }
import silk.json.codec { Serialize }
import silk.json.object { JsonObject }
import silk.json.output { JsonOptions }
import silk.json.scanner { JsonError }
import silk.json.value { Json }
import silk.io.writer { Writer, WriterError }
struct CaptureWriter {
bytes: [u8; 64],
length: usize,
}
impl Writer for CaptureWriter {
effect fn writeAll(self: &mut Self, value: &[u8]) -> ()
! WriterError
? &mut Writer {
let mut index: usize = 0
while index < value.length {
self.bytes[self.length] = value[index]
self.length = self.length + 1
index = index + 1
}
return ()
}
effect fn flush(self: &mut Self) -> ()
! WriterError
? &mut Writer {
return ()
}
}
fn holds(writer: &CaptureWriter, expected: &[u8]) -> bool {
if writer.length != expected.length {
return false
}
let mut index: usize = 0
while index < expected.length {
if writer.bytes[index] != expected[index] {
return false
}
index = index + 1
}
return true
}
struct Point {
x: i32,
y: i32,
}
impl Serialize for Point {
effect fn serialize(self: &Self, options: &JsonOptions) -> ()
! JsonError | WriterError
? &mut Writer {
return run JsonObject.begin(options) |> JsonObject.field("x", &self.x)
|> JsonObject.field("y", &self.y)
|> JsonObject.end
}
}
pub effect fn main() -> i32
! JsonError | WriterError {
let mut writer = CaptureWriter { bytes: [0; 64], length: 0 }
let point = Point { x: 3, y: -4 }
run Json.serialize(&point, &JsonOptions.compact()) |> Effect.provideMut<Writer>(&mut writer)
if !holds(&writer, b"{\"x\":3,\"y\":-4}") {
return 1
}
return 0
}Associated function write#
pub effect fn write(document: &Value) -> () ! JsonError | WriterError ? &mut WriterWrites document as compact JSON to the Writer. It does not flush the Writer.
Errors#
JsonErrorif the nesting is deeper than 64.WriterErrorif theWriterfails. Bytes that it accepted before the failure stay written.
Associated function writeWith#
pub effect fn writeWith(document: &Value, options: &JsonOptions)
-> () ! JsonError | WriterError ? &mut WriterWrites document as JSON to the Writer, with the indentation and depth limit of options.
It does not flush the Writer.
Errors#
JsonErrorif the nesting is deeper than the limit inoptions.WriterErrorif theWriterfails. Bytes that it accepted before the failure stay written.
Method field#
pub fn field<'a>(self: &'a Value, key: string) -> Option<&'a Value>Returns the value of member key of an object. Returns an absent option if self is not an
object or has no such key.
Method fieldAt#
pub fn fieldAt<'a>(self: Option<&'a Value>, key: string) -> Option<&'a Value>Returns the value of member key if self is a present object. Returns an absent option in
other cases.
When to use#
Use it to read nested fields in a chain: Json.field(&doc, "user") |> Json.fieldAt("name").
Method asI32#
pub fn asI32(self: Option<&Value>) -> Option<i32>Returns the number as i32. Returns an absent option if the value is absent, is not a number,
is not written as a plain integer (1.0 and 1e3 are not), or does not fit in i32.
Method asI64#
pub fn asI64(self: Option<&Value>) -> Option<i64>Returns the number as i64. Returns an absent option if the value is absent, is not a number,
is not written as a plain integer (1.0 and 1e3 are not), or does not fit in i64.
Method asU64#
pub fn asU64(self: Option<&Value>) -> Option<u64>Returns the number as u64. Returns an absent option if the value is absent, is not a number,
is not written as a plain integer (1.0 and 1e3 are not), or does not fit in u64.
Method asF64#
pub fn asF64(self: Option<&Value>) -> Option<f64>Returns the number as f64, rounded to the nearest value. Returns an absent option if the
value is absent, is not a number, or is too large for a finite f64.
Method asF32#
pub fn asF32(self: Option<&Value>) -> Option<f32>Returns the number as f32, rounded to the nearest value. Returns an absent option if the
value is absent, is not a number, or is too large for a finite f32.
Method asString#
pub fn asString<'a>(self: Option<&'a Value>) -> Option<string<'a>>Returns the text of a string. Returns an absent option if the value is absent or is not a string. The text is borrowed from the tree.
Implementation Serialize for Value#
impl Serialize for ValueOperation serialize#
effect fn serialize(self: &Self, options: &JsonOptions)
-> () ! JsonError | WriterError ? &mut WriterImplementation Deserialize for Value#
impl Deserialize for ValueOperation deserialize#
effect fn deserialize(scanner: &mut JsonScanner)
-> Self ! JsonError | OutOfMemoryError ? &mut AllocatorCategory: Data formats.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.