Независимая реализация RFC 8259. Покрывает все типы значений JSON, включая полную обработку escape-последовательностей (суррогатные пары, \uXXXX). По замыслу не поддерживает комментарии, висячие запятые и строки в одинарных кавычках. Свойство round-trip: Json.parse(v.into()) == v для любого корректного JsonValue.

ro v = Json.parse("{\"name\": \"alice\", \"age\": 30}")
match v.as_object() {
    Some(m) => {
        assert(m.get("name") == Some(Str("alice")))
        assert(m.get("age")  == Some(Num(30.0)))
    }
    None => assert(false)
}

Типы

JsonValue

стабильно с 0.1
export type JsonValue
    | Null
    | Bool(bool)
    | Num(f64)
    | Str(str)
    | Array([]JsonValue)
    | Object(HashMap[str, JsonValue])

Рекурсивный тип-сумма для произвольных документов JSON. Поддерживается структурное равенство — v1 == v2 работает для глубокого сравнения, включая вложенные массивы и объекты.

ParseJsonError

стабильно с 0.1
export type ParseJsonError
    | UnexpectedChar  { line int; col int; found char }
    | UnexpectedEof   { line int; col int }
    | InvalidEscape   { line int; col int; seq str }
    | InvalidUnicode  { line int; col int; hex str }
    | InvalidNumber   { line int; col int; text str }
    | DuplicateKey    { line int; col int; key str }
    | TrailingContent { line int; col int }

Все варианты несут 1-based line и col для диагностических сообщений.


Разбор

Json.parse

стабильно с 0.1
fn Json.parse(s str) Fail[ParseJsonError] -> JsonValue

Разбирает строку JSON. Бросает Fail[ParseJsonError] при любой лексической или синтаксической ошибке. Используйте блок with Fail[ParseJsonError] для обработки ошибок.

Примеры

ro arr = Json.parse("[1, 2, 3]")
assert(arr.is_array())

ro obj = Json.parse("{\"x\": true}")
match obj.as_object() {
    Some(m) => assert(m.get("x") == Some(Bool(true)))
    None    => assert(false)
}

Конструкторы

Удобные статические конструкторы для программного построения JsonValue:

fn JsonValue.null()                                  -> Self  // Null
fn JsonValue.bool(b bool)                            -> Self  // Bool(b)
fn JsonValue.num(n f64)                              -> Self  // Num(n)
fn JsonValue.str(s str)                              -> Self  // Str(s)
fn JsonValue.array(items []JsonValue)                -> Self  // Array(items)
fn JsonValue.object(fields HashMap[str, JsonValue])  -> Self  // Object(fields)

Аксессоры

fn JsonValue @as_bool()   -> Option[bool]
fn JsonValue @as_num()    -> Option[f64]
fn JsonValue @as_str()    -> Option[str]
fn JsonValue @as_array()  -> Option[[]JsonValue]
fn JsonValue @as_object() -> Option[HashMap[str, JsonValue]]

Каждый метод возвращает None, если вариант не совпадает. Комбинируйте с match или if Some:

ro v = Json.parse("42")
assert(v.as_num() == Some(42.0))
assert(v.as_str() == None)

Проверки типа

fn JsonValue @is_null()   -> bool
fn JsonValue @is_bool()   -> bool
fn JsonValue @is_num()    -> bool
fn JsonValue @is_str()    -> bool
fn JsonValue @is_array()  -> bool
fn JsonValue @is_object() -> bool

Сериализация

into — компактно

стабильно с 0.1
fn JsonValue @into() -> str

Сериализует в компактный JSON (одна строка, без лишних пробелов). Это реализация D73 Into[str]. Round-trip: Json.parse(v.into()) == v.

Примеры

ro v = Array([Num(1.0), Str("a")])
ro s str = v.into()
assert(s == "[1,\"a\"]")

pretty

стабильно с 0.1
fn pretty(v JsonValue) -> str

Печатает с отступом в 2 пробела. Не входит в Into[str] — вызывайте явно, когда нужен человекочитаемый вывод (логи, отладка). Для компактного канонического вывода используйте into().

Примеры

mut m = HashMap[str, JsonValue].new()
m.insert("name", Str("alice"))
ro s = pretty(Object(m))
assert(s.contains("\n"))  // multi-line