Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

jq 原樣輸出正常,執行篩選器卻改了 ID?加入 BigInt 前先檢查實際流程

原樣輸出測試可能通過,算術運算仍會改變末位數字。以成對的原始輸出作對照,在下游還原回呼掩蓋問題前找出資訊流失處。

八組 jq 與 Node 測試的精確數字結果示意
原創實測示意圖:八種路徑呈現提前保留與事後轉換的差異,僅代表本次 jq1.7/Node24.22.1 樣本。 Watch at Ease

ID 的末位改變時,先儲存每個步驟的原始輸出,再考慮更換解析器。原樣輸出測試通過,不能證明實際執行的算術篩選器也保留全部數字。

以下以八組本機測試區分來源文字、jq 輸出及 Node 解析三個邊界,並以字串預期值驗證完整數字。

1. 不要用 Number 儲存預期 ID

從原始 JSON 文字 {"id":9007199254740993,"count":42} 開始,將預期 ID 儲存為字串 "9007199254740993"。不要將它寫成 JavaScript 數字字面值,再比較兩個 Number:預期值本身可能已被捨入。修改資料前,記錄實際使用的 jq、Node 執行檔、版本及正式環境篩選器。

RFC8259 允許實作限制數字範圍和精度,並指出絕對值不超過 2^53−1 的整數可精確互通。超出此範圍,部分整數仍可表示,並非所有大數都會改變。識別碼需要完整數字;解析成功或紀錄看似合理,都不能作為驗收依據。

2. 成對測試原樣輸出與實際篩選器

八組 Linux 本機測試使用 jq1.7 與 Nodev24.22.1。對於上述樣本,jq -c '.' 輸出原始的 ...993;篩選器 .id += 0 則輸出 ...992。1.7 版手冊分別說明未修改字面值的保留方式與雙精度算術。因此,原樣輸出通過只證明該項操作,不能證明所有篩選器都安全。

儲存每次轉換前後直接輸出的文字。在這次執行檔上,.id |= tostring 得到 "9007199254740993";.id += 0 | .id |= tostring 得到 "9007199254740992"。提前與延後轉字串是兩種不同對照。請使用實際正式環境篩選器與建置版本重測;這些結果不能替其他 jq 實作或版本作保證。

3. 證明還原回呼實際收到什麼

一般 JSON.parse 後呼叫 id.toString() 得到 ...992;對已解析的值使用 BigInt 也得到 ...992。依鍵名讀取 BigInt(context.source) 的還原回呼,可以從原始樣本及原樣篩選器輸出取得 ...993。經過 jq 算術運算後,同一回呼卻得到 ...992,因為其輸入數字文字已改變。

對結構已知的數字 ID,測試回呼為 (k,v,c)=>k==='id'&&typeof v==='number'?BigInt(c.source):v。它僅適用於樣本,並非通用驗證器。應檢測實際執行環境是否支援 context.source,並驗證欄位路徑與整數字面值語法。小數或指數形式需要另行約定;把所有貌似數字的字串都轉換,也會誤改應保留為文字的欄位。

4. 完成整個往返驗證

如果能控制 API,遷移前先與接收端約定字串 ID。第八組測試明確使用 id.toString() 序列化還原出的 BigInt,再依字串協定解析,取得全部原始數字。這證明樣本能往返,並不代表相容於未變更的數字型別結構。避免悄悄改變數量、小數金額或無關欄位。

只有擷取的來源數字文字、每個預期傳遞步驟,以及最終依欄位協定進行的字串比較完全一致,才能驗收。保留實際篩選器回歸測試與原樣輸出對照,記錄 ...993 與 ...992,以及最先發生變化的位置。下游回呼無法恢復上游流失的資訊。本次測試使用本機樣本,未測試線上閘道或瀏覽器;請在實際產生端與使用端的邊界分別重測。

來源數字、jq 輸出及 Node 解析三個邊界示意
原創診斷示意圖:逐段比較數字文字,context.source 只能讀取目前解析輸入,無法還原更早流失的資訊。 Watch at Ease