///|
/// A JSON value with serde_json semantics: numbers keep their integer/float
/// classification and objects iterate in key order (serde_json without the
/// `preserve_order` feature, as used by typify).
pub(all) enum Value {
Null
Bool(Bool)
Number(Number)
String(String)
Array(Array[Value])
Object(@collections.StrMap[Value])
} derive(Debug, Eq)
///|
pub fn Value::is_null(self : Value) -> Bool {
self is Null
}
///|
pub fn Value::is_boolean(self : Value) -> Bool {
self is Bool(_)
}
///|
pub fn Value::is_number(self : Value) -> Bool {
self is Number(_)
}
///|
pub fn Value::is_string(self : Value) -> Bool {
self is String(_)
}
///|
pub fn Value::is_array(self : Value) -> Bool {
self is Array(_)
}
///|
pub fn Value::is_object(self : Value) -> Bool {
self is Object(_)
}
///|
pub fn Value::is_u64(self : Value) -> Bool {
self is Number(n) && n.is_u64()
}
///|
pub fn Value::is_i64(self : Value) -> Bool {
self is Number(n) && n.is_i64()
}
///|
pub fn Value::is_f64(self : Value) -> Bool {
self is Number(n) && n.is_f64()
}
///|
pub fn Value::as_bool(self : Value) -> Bool? {
match self {
Bool(b) => Some(b)
_ => None
}
}
///|
pub fn Value::as_str(self : Value) -> String? {
match self {
String(s) => Some(s)
_ => None
}
}
///|
pub fn Value::as_array(self : Value) -> Array[Value]? {
match self {
Array(a) => Some(a)
_ => None
}
}
///|
pub fn Value::as_object(self : Value) -> @collections.StrMap[Value]? {
match self {
Object(o) => Some(o)
_ => None
}
}
///|
pub fn Value::as_u64(self : Value) -> UInt64? {
match self {
Number(n) => n.as_u64()
_ => None
}
}
///|
pub fn Value::as_i64(self : Value) -> Int64? {
match self {
Number(n) => n.as_i64()
_ => None
}
}
///|
/// Like serde_json's `as_f64`: any number converts, other values do not.
pub fn Value::as_f64(self : Value) -> Double? {
match self {
Number(n) => Some(n.as_f64())
_ => None
}
}
///|
/// Look up a key of an object value.
pub fn Value::get(self : Value, key : StringView) -> Value? {
match self {
Object(o) => o.get(key)
_ => None
}
}
///|
/// Build an object value from pairs (later duplicates win).
pub fn Value::object(pairs : ArrayView[(String, Value)]) -> Value {
Object(@collections.StrMap::from_array(pairs))
}
///|
pub fn Value::from_u64(n : UInt64) -> Value {
Number(PosInt(n))
}
///|
pub fn Value::from_i64(n : Int64) -> Value {
Number(Number::from_i64(n))
}
///|
/// Non-finite floats become `Null`, like serde_json's `From`.
pub fn Value::from_f64(d : Double) -> Value {
match Number::from_f64(d) {
Some(n) => Number(n)
None => Null
}
}
///|
/// Compact JSON text, identical to serde_json's `Display` / `to_string`.
pub impl Show for Value with fn output(self, logger) {
logger.write_string(self.to_string())
}
///|
pub fn Value::to_string(self : Value) -> String {
let buf = StringBuilder()
write_compact(buf, self)
buf.to_string()
}
///|
/// Pretty JSON text, identical to serde_json's `to_string_pretty`.
pub fn Value::to_string_pretty(self : Value) -> String {
let buf = StringBuilder()
write_pretty(buf, self, 0)
buf.to_string()
}
///|
/// Convert to MoonBit's builtin `Json`. Integers outside the exactly
/// representable range keep their text in `repr`.
pub fn Value::to_json(self : Value) -> Json {
match self {
Null => Json::null()
Bool(b) => Json::boolean(b)
Number(n) => {
let d = n.as_f64()
match n {
Float(_) => Json::number(d)
_ => {
let text = n.to_string()
if d.to_string() == text {
Json::number(d)
} else {
Json::number(d, repr=text)
}
}
}
}
String(s) => Json::string(s)
Array(xs) => Json::array(xs.map(x => x.to_json()))
Object(o) => {
let m : Map[String, Json] = Map([])
for k, v in o {
m[k] = v.to_json()
}
Json::object(m)
}
}
}
///|
/// Convert from MoonBit's builtin `Json`. Numbers with a preserved lexeme are
/// re-classified from it; otherwise integral values within 2^53 become
/// integers and everything else a float.
pub fn Value::from_json(json : Json) -> Value {
match json {
Null => Null
True => Bool(true)
False => Bool(false)
Number(d, repr~) => {
if repr is Some(text) {
try parse(text) catch {
_ => ()
} noraise {
Number(n) => return Number(n)
_ => ()
}
}
let limit = 9007199254740992.0
if d == d.floor() && d.abs() <= limit {
if d >= 0.0 {
Number(PosInt(d.to_uint64()))
} else {
Number(NegInt(d.to_int64()))
}
} else {
Value::from_f64(d)
}
}
String(s) => String(s)
Array(xs) => Array(xs.map(Value::from_json))
Object(m) => {
let o = @collections.StrMap::new()
for k, v in m {
o.set(k, Value::from_json(v))
}
Object(o)
}
}
}