文件

核心概念

本頁說明 NanoJSON 如何將一份 JSON 文件建模為可即時編輯的 DOM 樹,以及型別系統的實際行為(含邊界情況)。

JSONEditor:根控制器

JSONEditorsrc/model/JSONEditor.js)是唯一的公開入口。建構時會:

  1. config.css 注入樣式表(loadCSS
  2. 建立 Lifecycle 實例並掛上 when.* 回呼
  3. 透過 getJSON() 載入初始資料(json / file / path
  4. #jsonToChildren() 將載入的資料遞迴轉換為 JSONEditorNode 陣列
  5. 掛載至 config.id 指定的既有元素,或建立新的 <section class="pd-json-editor">

若載入後 children.length < 1,會自動呼叫一次 insert() 新增一個空節點(JSONEditor.js:153-155)。

JSONEditorNode:遞迴節點

JSONEditorNodesrc/model/JSONEditorNode.js)代表樹中的單一鍵值對或陣列項目,持有 key / type / value / children / collapsed。每個節點的 DOM 由 #create() 局部渲染並快取於私有欄位 #dom;新增(#add)與移除(#remove)子節點時,只替換受影響的子樹,不重繪整棵樹。

型別系統與邊界情況

型別由 getType()src/data.js)依 JavaScript 執行期型別推導:

JS 值 getType() 回傳
Array.isArray(v) 為真 "array"
typeof v === "object"null "object"
typeof v === "boolean" "boolean"
typeof v === "number" "number"
其他 "string"

注意:getType(null) 回傳 "object",因為 JavaScript 的 typeof null === "object"。是否顯示為 null 型別節點,取決於呼叫端如何處理這個回傳值,而兩個呼叫點的處理方式並不一致:

這代表:{"a": null} 匯入後會渲染為型別下拉選單顯示 null 的節點;而 [null] 匯入後會渲染為一個內容為文字 "null"string 節點。若需要陣列中保留真正的 null 型別,需在應用層自行處理這個差異。

序列化:json getter 與 export() 的差異

JSONEditorNode.json(getter)遞迴呼叫私有的 #json(),依節點型別回傳對應的 JavaScript 值(array 回傳陣列、object 回傳物件、boolean/number 做型別轉換、其餘回傳字串)。

根層級的序列化則有兩種路徑,行為並不完全相同:

路徑 篩選條件(JSONEditor.js 行為
editor.json(getter,第 267-277 行) 只收 e.key 為真的子節點 沒有鍵的子節點(例如根為陣列或單一純量時的節點)會被忽略
editor.export()(第 289-310 行) e.key 為真,children.length === 1 時也收 根層只有一個無鍵子節點時(陣列根、純量根)仍會被寫入輸出檔案

也就是說:若根節點目前是一個陣列(type === "array" 且只有一個子節點且該子節點無 key),editor.json 會回傳 "{}",但 editor.export() 卻能正確下載出該筆資料。呼叫 editor.json 前,若根型別可能是陣列,需注意這個差異。

唯讀模式

readonly 設定不是逐節點傳遞的旗標,而是在初始化完成後,透過 document.querySelectorAll(selectorSubNodes)button:not(.collapsed), textarea, input, select)對整棵已渲染的 DOM 樹批次加上 / 移除 disabled 屬性(見 enable() / disable()JSONEditor.js:171-183)。切換唯讀狀態不會觸發 Lifecycle 的任何鉤子。

延伸閱讀

EN