basic JSON API
JSON 基本类型API
enum JsVal:
case JsNull
case JsBoolean(value: Boolean)
case JsNumber(value: Double)
case JsString(value: String)
case JsArray(elements: List[JsVal])
case JsObject(fields: Seq[String, JsVal])
wjson 提供的是基于 immutable 的 API
JsObject 会保留构建时字段的顺序,在格式化输出时,保留该顺序。但 equals 方法会忽略字段的顺序。
JSON对象
wjson 提供了多种方式来构造 JSON 值。
import wjson.{*, given}
// JSON Object: {"name":"John","age":18} 下面的多种方式,都可以构造出同一个 JSON 对象
val objStr = """{"name":"John","age":18}"""
val obj1 = JsObject(Seq("name" -> JsString("John"), "age" -> JsNumber(18))) // 使用 JsObject 构造器
val obj2 = JsObject(Seq("name" -> "John", "age" -> 18)) // 通过隐式转换,将基本类型转换为 JsValue
val obj3 = JsValue.obj("name" -> "John", "age" -> 10) // 使用 JsValue.obj 构造 JsObj, 更简洁
val obj4 = JsValue.parseJson(objStr) // 作为 JSON 字符串解析
val obj5 = JsValue.parseJson5(objStr) // 作为 JSON5 字符串解析
val obj6 = json"""{"name":"John","age":18}""" // 使用 json"..." 字符串插值
val obj7 = json5"{name:'John', age:18 }" // 使用 json5"..." 字符串插值, 注意 JSON5 的语法更宽松
// JSON Array: [1,2,3]
val arr1 = JsArray(Seq( JsNumber(1), JsNumber(2), JsNumber(3) )) // 最基本的构造方式
val arr2 = JsArray(Seq(1,2,3)) // 通过隐式转换,将基本类型转换为 JsValue
val arr3 = JsValue.arr(1,2,3) // 使用 JsValue.arr 构造 JsArray, 更简洁
val arr4 = JsValue.parseJson("[1,2,3]") // 作为 JSON 字符串解析
val arr5 = JsValue.parseJson5("[1,2,3, ]") // 作为 JSON5 字符串解析, JSON5 支持末尾的逗号
val arr6 = json"[1,2,3]" // 使用 json"..." 字符串插值
val arr7 = json5"[1,2,3, ]" // 使用 json5"..." 字符串插值, 注意 JSON5 的语法更宽松
基本 API
-
输出 JSON 字符串
js1.show // 输出 JSON 字符串 js1.showPretty // 输出格式化的 JSON 字符串 js1.show(indent = 4) // 输出格式化的 JSON 字符串,缩进为4个空格
-
JsObject 操作
obj1.field("name") // 类似于 Map 的 apply 方法,获取字段值 obj1.filedOpt("name") // 类似于 Map 的 get 方法,获取字段值,返回 Option obj1.contains("name") // 判断是否包含某个字段 obj1.keys // 获取所有的字段名 obj1 ++ obj2 // 合并两个 JsObject, 如果有重复的字段,后者覆盖前者 obj1 ++ Seq("name" -> "Tom", "age" -> 20) // 合并 JsObject 和 Map[String, Any] obj1 + ("name" -> "Tom") // 添加一个字段 obj1.remove("field1", "field2") // 删除字段
-
JsArray 操作
arr1.elements // 获取所有的元素 arr1(0) // 获取第一个元素 arr1 ++ arr2 // 合并两个 JsArray arr1 ++ Seq(1,2,3) // 合并 JsArray 和 Seq[T] arr1 :+ 4 // 在尾部添加一个元素,返回新的 JsArray 0 +: arr1 // 在头部添加一个元素,返回新的 JsArray arr1.updated(1, 100) // 更新第二个元素的值为 100,返回新的 JsArray
-
JsValue 操作
js1 match case JsNull => // JsNull 的模式匹配 case JsBoolean(b) => b // JsBoolean 的模式匹配 case JsNumber(n: Long) => n // JsNumber 的模式匹配 case JsNumber(n: Double) => n // JsNumber 的模式匹配 case JsString(s) => s // JsString 的模式匹配 case JsObj(fields) => fields // JsObj 的模式匹配 case JsArr(elements) => elements // JsArr 的模式匹配 js1.isObj // 判断是否是 JsObj js1.isArr // 判断是否是 JsArr js1.isNull // 判断是否是 JsNull js1.isBoolean // 判断是否是 JsBoolean js1.isNumber // 判断是否是 JsNumber js1.isString // 判断是否是 JsString js1 == js2 // 判断是否相等, 对 JsObject, 会忽略字段的顺序 js1.asBool // 强制转换为 JsBoolean,如果类型不匹配,会抛出异常 js1.asNum // 强制转换为 JsNumber,如果类型不匹配,会抛出异常 js1.asStr // 强制转换为 JsString,如果类型不匹配,会抛出异常 js1.asObj // 强制转换为 JsObject,如果类型不匹配,会抛出异常 js1.asArr // 强制转换为 JsArray,如果类型不匹配,会抛出异常
-
JSON 与 对象值之间的转换
case class Person(name: String, age: Int) derives JsValueMapper // 通过 derives JsValueMapper 自动生成 JSON 映射 case class User(name: String, age: Int) // 未定义 JsValueMapper 也可以,但每次映射都会生成 JsValueMapper 实例, 会增加编译后的代码大小 val js1 = json"""{"name":"John","age":18}""" val person = js1.toBean[Person] // 反序列化为 Person val user = js1.toBean[User] // 反序列化为 User val js2 = person.toJson // 序列化为 JsValue
-
字符串插值
val name = "John" val age = 18 val obj1 = json"""{"name": $name, "age": $age}""" // 使用 json"..." 字符串插值 val obj2 = json5"{name: $name, age: $age }" // 使用 json5"..." 字符串插值, 注意 JSON5 的语法更宽松 val arr1 = json"[1,2,3, $age]" // 使用 json"..." 字符串插值 val arr2 = json5"[1,2,3, $age, ]" // 使用 json5"..." 字符串插值, 注意 JSON5 的语法更宽松
字符串插值也可以用于进行简单的模式匹配:
val js = json"{name: 'John', age: 18, }" jsval match case json"{name: $name, age: $age}" => println(name, age) // John 18 case _ => println("not match") json5"{ name: 'John', age: 18, address: { city: 'Beijing', country: 'China' }, scores: [80,90,100] }" match case json5"{ address: {city: $city}, scores:[80,$score1,$score2]}" => println(city, score1, score2) // Beijing 90 100 case _ => println("not match")
使用字符串插值进行模式匹配时,提供了一定的灵活性,例如:
- 对 JsObject, 在模式中可以仅匹配部分字段,而忽略其他字段。
- 对 JsArray, 可以匹配前面部分元素,而忽略其他元素。
需要更强大的模式匹配功能,可以使用 wjson-pattern 模块。