Wing 标准库 Json 类型 API 全解析:从 hangar 测试快照到宏展开实现
编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载Json是 Wing 语言内置的云上通用数据格式类型用于承载对象、数组以及各类 JSON 原始值。本文以仓库中 json.test.w 的 sim 平台测试快照 为主线完整梳理Json/MutJson的 11 个测试用例、每个 API 的语义与边界行为并深入 SDK 源码解读其macro展开机制。读完本文你将掌握 Wing 中 JSON 数据的读写、解析、序列化、深拷贝与容错转换的完整实战技能并能理解 hangar 快照是如何验证这些行为的。快照文档概览一次完整的 Json 测试运行记录关联文档是 hangarWing 工具链测试套件生成的 Markdown 快照标题为[json.test.w](https://link.gitcode.com/i/72ff0633f6ce56dcc19108100209988a) | test | sim表明它记录了源文件 tests/sdk_tests/std/json.test.w 在sim本地模拟器平台上执行wing test的 stdout 输出。快照正文stdout.log完整列出 11 个测试全部通过的结果pass ─ json.test.wsim » root/Default/test:deepCopy(), deepCopyMut() pass ─ json.test.wsim » root/Default/test:delete() for MutJson pass ─ json.test.wsim » root/Default/test:get() pass ─ json.test.wsim » root/Default/test:getAt() pass ─ json.test.wsim » root/Default/test:has() pass ─ json.test.wsim » root/Default/test:keys(), values(), entries() pass ─ json.test.wsim » root/Default/test:parse() pass ─ json.test.wsim » root/Default/test:set() pass ─ json.test.wsim » root/Default/test:setAt() pass ─ json.test.wsim » root/Default/test:stringify() pass ─ json.test.wsim » root/Default/test:tryParse() Tests 11 passed (11) Snapshots 1 skipped Test Files 1 passed (1) Duration DURATION注意其中Duration DURATION是快照去敏处理的结果hangar 在 tools/hangar/src/utils.ts 的sanitizeOutput()中会把形如Duration 1m23.456s的时间戳统一替换为DURATION占位符保证快照在不同机器上可稳定比对该正则位于 utils.ts 第 71 行附近。Snapshots 1 skipped则表明本次运行跳过了单个无关快照比对属正常输出。测试用例全景11 个测试覆盖的 API 面测试名覆盖 API类型has()Json.has/MutJson.has只读get()Json.get/MutJson.get只读含错误路径getAt()Json.getAt/MutJson.getAt只读含越界路径set()MutJson.set写入setAt()MutJson.setAt写入stringify()Json.stringify含indent选项序列化keys(), values(), entries()Json.keys/Json.values/Json.entries枚举parse()Json.parse解析tryParse()Json.tryParse容错解析deepCopy(), deepCopyMut()Json.deepCopy/Json.deepCopyMut拷贝delete() for MutJsonMutJson.delete写入这些用例完整对应标准库 API 参考 docs/api/04-standard-library/std/json.md 中Json与MutJson两类的全部公开方法以及语言参考 docs/api/05-language-reference.md 第 1.1.4 节Json type描述的能力。Json 与 MutJson可变与不可变的双类型设计Wing 的 JSON 支持建立在两个互补类型之上二者在 packages/winglang/sdk/src/std/json.ts 中定义Json不可变注释为 Immutable Json表示不可变的无类型 JSON 值可跨 inflight 上下文引用。其构造函数是私有的只能通过字面量或静态工厂方法获得。MutJson可变注释为 Mutable Json提供set、setAt、delete等写操作。语言参考指出设计动机JSON 是云上的线协议Wing 为此提供内置支持但 Wing 是静态类型语言而 JSON 是动态类型数据因此需要桥接。字面量语法非常直观Json关键字在对象字面量中可省略let jsonString Json hello; let jsonNumber Json 123; let jsonBool Json true; let jsonArray Json [ 1, 2, 3 ]; let jsonObj Json { boom: 123 }; let jsonMutObj MutJson { hello: 123, world: [ 1, cat, 3 ], // 异构数组 boom boom: { hello: 1233 } // 非符号键 };测试文件 tests/sdk_tests/std/json.test.w 大量使用这种字面量语法构造被测数据。逐用例解析从测试代码看每个 API 的语义has()键存在性判断test has() { let obj Json { key1: 1, key2: 2}; assert(obj.has(key1) true); assert(obj.has(key3) false); }has(key)底层宏为((obj, key) { return obj.hasOwnProperty(key); })($self$,$args$)即借用 JavaScript 的hasOwnProperty判断键是否存在不区分值类型。get()取值与键不存在错误let JSON_PROPERTY_DOES_NOT_EXIST_ERROR Json property \c\ does not exist; let obj Json { a: 1, b: 2 }; let mutObj MutJson { a: 1, b: 2 }; assert(obj.get(b) 2); assert(mutObj.get(b) 2); assertThrows(JSON_PROPERTY_DOES_NOT_EXIST_ERROR, () { obj.get(c); }); assertThrows(JSON_PROPERTY_DOES_NOT_EXIST_ERROR, () { mutObj.get(c); });测试自定义了assertThrows(expected, block)辅助函数通过try/catch捕获并断言异常消息。get()的宏定义为若obj[key] undefined则抛出Json property ${key} does not exist否则返回该值。Json.get返回Json而MutJson.get返回MutJson二者错误语义一致。getAt()数组索引与越界错误let INDEX_OUT_OF_BOUNDS_ERROR Index out of bounds; let jsonArray Json [foo, bar, baz]; let mutJsonArray MutJson [1, 2, 3]; assert(jsonArray.getAt(2) baz); assert(mutJsonArray.getAt(2) 3); assertThrows(INDEX_OUT_OF_BOUNDS_ERROR, () { jsonArray.getAt(3); mutJsonArray.getAt(3); });getAt()的宏检查obj[index] undefined时抛出Index out of bounds合法索引则直接返回值体现了Json数组与 Wing 原生Array越界行为的差异化约定。set() / setAt()可变容器的写入test set() { let mutObj MutJson { x: 1, y: 2 }; mutObj.set(x, -1); // 覆盖已有键 mutObj.set(z, 3); // 新增键 assert(mutObj.get(x) -1); assert(mutObj.get(z) 3); } test setAt() { let mutJsonArray MutJson [1, 2, 3]; mutJsonArray.setAt(0, -1); // 覆盖索引 0 mutJsonArray.setAt(3, 3); // 追加到索引 3长度从 3 扩展到 4 assert(mutJsonArray.getAt(0) -1); assert(mutJsonArray.getAt(3) 3); }set的宏为((obj, key, value) { obj[key] value; })($self$, ...$args$)setAt类似地按索引赋值。注意setAt(3, 3)展示了 JS 数组动态扩容特性在MutJson数组上的延续。stringify()序列化与缩进选项let obj Json { a: 1, b: 2 }; let stringified Json.stringify(obj); let stringifiedIndent Json.stringify(obj, indent: 2); assert(stringified {\a\:1,\b\:2}); assert(stringifiedIndent {\n \a\: 1,\n \b\: 2\n});stringify的宏为((json, opts) { return JSON.stringify(json, null, opts?.indent) })(...$args$)第二个参数是JsonStringifyOptions结构体唯一字段indent: num表示缩进空格数。该结构体定义在 json.ts 第 6-9 行。keys() / values() / entries()枚举三件套let obj Json { a: 1, b: [3, 7, 9], c: { foo: bar } }; let entries Json.entries(obj); let keys Json.keys(obj); let values Json.values(obj); let var i 0; for e in entries { assert(e.key keys.at(i)); assert(e.value values.at(i)); i 1; }keys()宏为Object.keys($args$)values()宏为Object.values($args$)二者返回Arraystr与ArrayJsonentries()是唯一不是宏的静态方法直接由Object.entries(json).map(([key, value]) ({ key, value }))实现返回JsonEntry[]JsonEntry结构体包含key: str与value: Json两个字段。测试通过for循环交叉断言三者的对应关系验证了枚举顺序的一致性。语言参考中另注values()目前返回数组内的Json对象因为 Wing 尚不能表示异构数组。parse()严格解析及其已知限制test parse() { assert(Json.parse(123) 123); assert(Json.parse(true) true); assert(Json.parse(\foo\) foo); // 以下断言被注释编译器层面待修复issue #4131 // assert(Json.parse([1,5,false]) [1,5,false]); // assert(Json.parse(String) obj); // null 解析待 Json null 支持落地后启用issue #1819 // assert(Json.parse(null) null); }parse宏为JSON.parse($args$)即原生 JSON 解析。测试代码中的注释如实记录了仓库的已知边界数组与对象的全等比较、null字面量尚处于未实现状态分别对应上游 issue #4131 与 #1819。这些注释本身就是重要的工程信息——写文章或排障时不应假设这些能力已可用。tryParse()容错解析与nil语义test tryParse() { assert(Json.tryParse(123) 123); assert(Json.tryParse(true) true); assert(Json.tryParse(\foo\) foo); assert(Json.tryParse(foo) nil); // 非法 JSON 字符串 assert(Json.tryParse() nil); // 空字符串 assert(Json.tryParse(nil) nil); // 未传参数 }tryParse的宏为((args) { try { return (args undefined) ? undefined : JSON.parse(args); } catch (err) { return undefined; } })($args$)解析失败或传入undefined时返回undefined在 Wing 中映射为可选类型Json?即nil语义不会抛异常。这使其成为安全解析外部输入的推荐入口。deepCopy() / deepCopyMut()深拷贝与隔离验证test deepCopy(), deepCopyMut() { let original Json ({ string: wing, number: 123, array: [1, 2, 3], true: true, false: false, object: { key1: value1, key2: 2, key3: false, key5: [3, 2, 1] } }); let mutation Json{ key1: 1, key2: 2}; let copy Json.deepCopy(original); // 不可变深拷贝 let copyMut Json.deepCopyMut(original); // 可变深拷贝 assert(copy copyMut); copyMut.set(object, mutation); assert(copy ! copyMut); // 修改可变副本不影响不可变副本 assert(copyMut.get(object) mutation); }deepCopy与deepCopyMut的宏相同均为JSON.parse(JSON.stringify($args$))即通过序列化再反序列化实现真正的深拷贝区别仅在于返回类型deepCopy返回不可变的JsondeepCopyMut返回可变的MutJson。测试用嵌套数组/对象的复合数据验证了拷贝深度并演示了对可变副本set后两个副本不再相等证明数据隔离成立。delete() 与 MutJson 的可变写操作test delete() for MutJson { let mutObj MutJson { x: 1, y: 2 }; mutObj.delete(x); assert(mutObj.has(x) false); assert(mutObj.has(y)true); assert(mutObj.delete(random key that doesnt exist) true); }MutJson.delete(key)的宏为(delete ($self$)[$args$])返回布尔值。测试最后一个断言揭示了一个值得注意的语义删除不存在的键也会返回true因为delete操作本身不报错。这与get()的严格抛错形成鲜明对比调用方应按此语义设计逻辑。语言参考中还提示Json.delete静态方法只接受MutJson作为目标对不可变Json调用会在编译期报错expected JsonMut。实现机制macro注释驱动的编译期展开Json/MutJson的绝大多数方法体都是throw new Error(Macro)占位真正的实现以macroJSDoc 注释形式声明由 Wing 编译器在编译期展开为内联 JavaScript。这种设计在 packages/winglang/sdk/src/std/json.ts 中随处可见例如方法展开宏Json.keysObject.keys($args$)Json.valuesObject.values($args$)Json.stringifyJSON.stringify(json, null, opts?.indent)Json.parseJSON.parse($args$)Json.deepCopy/deepCopyMutJSON.parse(JSON.stringify($args$))Json.hasobj.hasOwnProperty(key)Json.get键不存在时抛Json property ${key} does not existJson.getAt索引越界时抛Index out of boundsJson.tryParsetry { JSON.parse(args) } catch { undefined }MutJson.set/setAtobj[key] value/obj[idx] valueMutJson.deletedelete obj[key]理解宏展开机制就能解释测试中观察到的全部行为get的报错文案、getAt的越界判断、tryParse对非法输入的静默容错以及stringify的缩进参数全部来自这些一行宏。这既是 Wing 静态类型 JSON 动态数据桥接设计的核心也是测试快照断言能够精确到错误消息的原因。类型转换补充asStr / asNum / asBool 与 fromJson 族测试未直接覆盖、但同属JsonAPI 面的还有类型安全转换方法它们在 API 参考与语言参考中均有明确约定实战中常与get()联用asStr()/asNum()/asBool()强制转换类型不匹配时抛出unable to parse typeof value as a type错误tryAsStr()/tryAsNum()/tryAsBool()对应的容错版本类型不匹配返回undefinedstr.fromJson()/num.fromJson()等静态方法将Json安全地转回强类型默认执行 schema 校验每个fromJson都有对应的tryFromJson()返回可选类型可通过unsafe: true关闭校验JsonValidationOptions。例如语言参考给出的错误示例str.fromJson(jsonNumber)会触发运行时错误unable to parse number 123 as a string.。结构体也可通过Foo.fromJson(jFoo)从Json构造详见 docs/by-example/23-Json.md 的 by-example 示例。hangar 快照机制快照如何生成与复现这份快照并非手写而是 hangar 测试框架自动生成的产物。在 tools/hangar/src/generated_test_targets.ts 中testTest()以[sim]为平台调用wing test即wing test sim 目标输出捕获到fileMap[stdout.log]之后createMarkdownSnapshot()生成形如xxx.test.w_test_sim.md的快照文件sanitizeOutput()tools/hangar/src/utils.ts会替换动态内容将Duration 1m23s这类不稳定文本统一为DURATION这正是快照第 20 行占位符的来源。因此任何开发者在本仓库根目录执行对应测试命令后都可以复现快照中的 11 个 pass 结果。快照目录tools/hangar/__snapshots__/test_corpus/sdk_tests/std/json.test.w_test_sim.md与源测试tests/sdk_tests/std/json.test.w一一对应属于测试即文档的工程实践。编译侧验证tf-aws 快照补充说明同目录下还有编译快照 tools/hangar/snapshots/test_corpus/sdk_tests/std/json.test.w_compile_tf-aws.md记录的是wing compiletf-aws目标的产物main.tf.json。由于json.test.w中所有逻辑都包裹在test ... {}块内、且仅使用Json标准库类型bring cloud只用于引入测试框架依赖编译产物中没有任何 AWS 资源定义main.tf.json只有空的 provider 与 backend 元数据。这从编译侧印证纯Json数据操作在 Wing 中不产生基础设施资源属于纯运行时代码。总结与进一步阅读通过这份 hangar 快照及其背后的测试源码可以确认 WingJson/MutJson已覆盖以下能力闭环键值读写has/get/set/delete、数组索引getAt/setAt、枚举keys/values/entries、序列化stringify含缩进、解析parse/tryParse与深拷贝deepCopy/deepCopyMut并明确了键不存在、索引越界、类型不匹配等边界错误语义。若需继续深入推荐按顺序阅读测试源码tests/sdk_tests/std/json.test.wSDK 实现packages/winglang/sdk/src/std/json.tsAPI 参考docs/api/04-standard-library/std/json.md语言参考 Json 章节docs/api/05-language-reference.md入门示例docs/by-example/23-Json.md快照生成机制tools/hangar/src/generated_test_targets.ts赞分享编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载相关推荐Wing 标准库 Bucket.copy() 的 sim 测试快照解析从 Hangar 快照输出到 SDK 实现Wing 标准库 Bucket.copy 的 sim 测试快照解析从 Hangar 快照输出到 SDK 实现 本篇技术指南围绕 Wing 仓库中一份自动化 E编程语言编译器云原生语言运行时Wing 标准库 math.isPrime 素数检测从实现原理到 hangar 测试快照全解析Wing 标准库 math.isPrime 素数检测从实现原理到 hangar 测试快照全解析 本篇文章以 Wing 语言标准库 math 模块的素数检测函数编程语言编译器云原生语言运行时Wing 标准库 util.os() 平台检测实战从 SDK 实现到 Hangar 快照测试全解析Wing 标准库 util.os 平台检测实战从 SDK 实现到 Hangar 快照测试全解析 util.os 是 Wing 标准库 util 模块中用于获取编程语言编译器云原生语言运行时创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考