- 桌面应用
- 开发工具
【免费下载链接】MooTool
Handy tool set for developers
本篇技术指南以 DIFF-430 文档 为核心,解析 MooTool 下一代 Compose(Desktop)实现如何将 JSONPath 路径选择器与检查器内联树的节点预览,对齐 Electron 端
JsonPathPicker的formatPreview行为,并理清「路径预览」与「JSONPath 查询」两条链路在字符串标量上的格式化差异。读完你将掌握:两条链路的输出规则与实现位置、单元素数组解包等边界行为,以及对应的单测与离线验证命令。
一、问题背景:预览区字符串为什么带引号
在 MooTool 的 Electron 实现中,JsonPathPicker弹层的右侧预览区调用formatPreview(value)渲染选中路径的节点值(见 JsonPathPicker.tsx):
- 值为
undefined(路径不存在)时,显示空字符串; - 值为字符串标量时,显示原文(例如
"One"只显示One,不带 JSON 引号); - 值为对象/数组时,使用
JSON.stringify(value, null, 2)缩进美化输出。
而 Compose 版本在 DIFF-430 之前,路径预览走的是queryPath/formatPathResult链路。这条链路为了与 Electron 的查询结果formatJsonPathValue对齐(见 jsonTools.ts),对字符串标量执行JSON.stringify(value),于是"One"会显示成带引号的"One"。这造成了同一份数据在弹层、内联树与查询结果之间显示不一致:预览区出现了 JSON 语法形态的引号,而 Electron 的预览区是"所见即所得"的原始文本。
二、行为定义:预览与查询各走各的格式化
DIFF-430 给出的最终行为划分清晰而明确:
| 场景 | 入口 | 字符串标量 | 对象 / 数组 | 路径不存在 |
|---|---|---|---|---|
| 路径选择器/内联树预览 | JsonEngine.pathPickerPreview+formatPickerPreview | 原文(无 JSON 引号) | pretty-print 缩进 | 空字符串 |
| JSONPath查询 | JsonEngine.queryPath+formatPathResult | 带 JSON 引号(JSON.stringify) | pretty-print 缩进 | "undefined" |
要点是:预览对齐 ElectronformatPreview;查询仍保持对齐 ElectronformatJsonPathValue,两者都是 Electron 侧的既有约定,Compose 侧只需把预览链路切换到新的实现即可。
三、Compose 源码级解析:pathPickerPreview与formatPickerPreview
核心实现位于 JsonEngine.kt,pathPickerPreview与formatPickerPreview两个函数共同构成新的预览链路。
fun pathPickerPreview(input: String, path: String, t: JsonTranslator): String { if (path.isBlank()) return "" val document = parsePreserving(input, t) return try { formatPickerPreview(JsonPath.using(jsonPathConfig).parse(document).read<JsonNode>(path.trim())) } catch (_: PathNotFoundException) { "" } catch (error: InvalidPathException) { throw JsonException(t.t("json.error.invalidPath", mapOf("reason" to (error.message ?: path)))) } catch (error: JsonPathException) { throw JsonException(t.t("json.error.invalidPath", mapOf("reason" to (error.message ?: path)))) } } private fun formatPickerPreview(result: JsonNode?): String { if (result == null || result.isMissingNode) return "" val node = if (result.isArray && result.size() == 1) result[0] else result if (node == null || node.isMissingNode) return "" if (node.isTextual) return node.textValue() return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node) }对照可提炼出四个关键行为:
- 空路径与空结果都返回空字符串:
path.isBlank()直接返回"";PathNotFoundException(路径不存在)捕获后同样返回""——与 ElectronformatPreview中value === undefined返回''语义一致。 - 字符串标量去引号:
node.isTextual时直接取node.textValue()原始文本,不再经过 Jackson 的writeValueAsString,从而去掉 JSON 引号。 - 对象/数组走 pretty-printer:非文本节点交给
mapper.writerWithDefaultPrettyPrinter(),等价于 Electron 的JSON.stringify(value, null, 2),输出带缩进的格式化 JSON。 - 单元素数组解包:
if (result.isArray && result.size() == 1) result[0],把仅含一个元素的数组退化为其唯一元素再格式化。这与查询链路formatPathResult中的解包逻辑完全一致,也与JsonEngine的 Jayway JSONPath 配置Option.ALWAYS_RETURN_LIST(见 JsonEngine.kt)互相配合——查询始终以列表形态返回,格式化前统一解包,保证两条链路对同一路径输出一致的数据形态。
值得留意的是formatPickerPreview与formatPathResult的对照:formatPathResult 对文本节点执行mapper.writeValueAsString(value.textValue())(输出带引号的"One"),缺失节点返回字面量"undefined";两条函数仅在文本分支与缺失分支上有意区分,这正是 DIFF-430 的核心差异点。
四、jsonPathNodePreview:预览链路的 UI 统一入口
pathPickerPreview是引擎层能力,UI 层统一通过jsonPathNodePreview调起,定义于 JsonInspectorResult.kt:
internal fun jsonPathNodePreview( input: String, path: String, translator: JsonTranslator, fallback: String = "", ): String = runCatching { JsonEngine.pathPickerPreview(input, path, translator) } .getOrElse { fallback }该 helper 用runCatching包裹引擎调用,任何解析/求值异常都会回退到调用方传入的fallback(通常是树行原有的entry.preview缩写)。DIFF-430 之前,jsonPathNodePreview内部走的是JsonEngine.queryPath(带引号语义),本次迁移后改走pathPickerPreview,从而让所有 UI 预览点一次性切换。
它的三个实际消费方均在 JSON 工具页内:
- 路径选择弹层右侧预览:见 JsonPathPickerDialog.kt,
previewText通过remember(session.editor.revision, previewPath)缓存,随编辑器修订号与选中路径失效重算。 - 检查器内联路径树:
JsonScreen中仅对选中行计算selectedPathPreview(见 JsonScreen.kt),单击行时将预览写入结果区;双击行则先预览再走performInlinePathTreeDoubleTapQuery执行真实查询(见 JsonInspectorResult.kt)。 - 重复键路径同步:结构面板命中重复键时,
jsonInspectorDuplicatePathClick复用jsonPathNodePreview生成预览并写入会话,再复制路径(见 JsonInspectorPathUi.kt)。
五、会话层行为:预览与查询互不污染
DIFF-430 的迁移离不开会话层两个函数的既有约定,均在 JsonInspectorResult.kt:
applyPathPickerChoice(弹层「使用」/双击共用):仅写入jsonPath、清空查询结果、设置pathApplied通知并关闭弹层,不执行 JSONPath 查询——这是 DIFF-319 对齐 Electron 的产物。applyInlinePathTreePreview(内联树单击):写入路径与预览到pathResult,同样不触发查询。
正因为弹层"只用不查"、内联树"单击预览、双击查询"的语义早已收敛,DIFF-430 才得以让预览链路独立切换为formatPickerPreview而无副作用;查询语义继续由queryPath/formatPathResult承担,保证结果区与历史记录中的查询输出仍是带引号的 JSON 形态。
六、单测验证与离线运行
新增与调整的测试集中在 JsonPathNodePreviewTest.kt,其中与本主题直接相关的断言包括:
preview_uses_pretty_json_for_objects:对$.store这类对象路径,预览包含键名与换行(pretty-print 生效);preview_string_scalar_matches_electron_formatPreview:对$.store.books[0].title路径,字符串标量预览断言等于"One"原文而非"\"One\"",正是 DIFF-430 的命名与验收标准;pathPickerPreview_empty_when_path_missing:对$.missing断言返回空字符串;preview_falls_back_when_json_invalid:输入非法 JSON(如{)时,jsonPathNodePreview返回调用方传入的 fallback。
验证命令与文档一致,在仓库next-compose目录下离线执行:
./gradlew :composeApp:desktopTest --offline七、演进脉络:从 DIFF-329 到 DIFF-430
把相邻变更记录串联起来,可以还原这条预览链路的完整演进史(均为next-compose/docs/diff/下文档):
- DIFF-329 引入
jsonPathNodePreview,但经由queryPath(带引号),首次统一弹层与pathResult; - DIFF-330 让内联路径树选中行也走
jsonPathNodePreview,替换listPaths的{n}/[n]缩写; - DIFF-331 改为仅对选中行求值,并用
remember(editor, jsonPath)缓存单条预览,规避大 JSON 下每条节点都查询的成本; - DIFF-430 即本篇主题:把预览链路从
queryPath切换为新增的pathPickerPreview/formatPickerPreview,彻底解决字符串标量预览带引号的问题。
八、适用前提与边界说明
- 本文描述的是 MooTool 下一代Compose Desktop(
next-compose)实现;Electron 参考实现在next/src/features/json/下,两者通过formatPreview语义对齐而非共享代码。 pathPickerPreview对非法路径(InvalidPathException/JsonPathException)仍抛出JsonException,由 UI 层jsonPathNodePreview的runCatching转译为 fallback 展示;只有PathNotFoundException被捕获后静默返回空串。- 预览链路与查询链路共用
JsonEngine的 JacksonObjectMapper(含withExactBigDecimals(true)的数值精度设置)与 Jayway JSONPathALWAYS_RETURN_LIST配置,格式化行为严格受这两个底层配置约束,调整它们会同时影响两条链路。
- 桌面应用
- 开发工具
【免费下载链接】MooTool
Handy tool set for developers
相关推荐
MooTool 源码解析:JSONPath 弹层与内联路径树节点预览如何对齐 Electron `formatPreview`
MooTool 源码解析:JSONPath 弹层与内联路径树节点预览如何对齐 Electron formatPreview 导读 本文围绕 MooTool Ne
桌面应用开发工具MooTool Compose 桌面版 JSONPath 选择器弹层对齐 Electron 布局:规格解析与实现验证
MooTool Compose 桌面版 JSONPath 选择器弹层对齐 Electron 布局:规格解析与实现验证 MooTool 在 Electron 前端
桌面应用开发工具MooTool Compose 版跨产品草稿迁移:Java `t_func_content` 字段对齐与工具会话恢复实战
MooTool Compose 版跨产品草稿迁移:Java t_func_content 字段对齐与工具会话恢复实战 本篇基于 DIFF 153 对齐文档 ht
桌面应用开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考