Delphi 開發這麼久,一直對 TFDMemTable 等 TDataSet 轉成 JSON 內容很有意見,在預設條件下,產出的是【特規】JSON 格式,如下圖所示:
為了方便說明,底下【特規 JSON 】一律使用【DJSON 】代換。
一般 JavaScript 前端大部份的物件所接受的 JSON 格式會是:
[{"EmpNo":2,"LastName":Nelson"}, {"EmpNo":4,"LastName":Eden"}]
Delphi 預設的格式不適用在前端,而 10.2 版開始有提供轉換函式來產出前端常用的 JSON 格式。
特規有特規的好處
當時為了達到這功能的我感到非常困惑,為什麼這功能要等這麼久才能實現?
後來想想,其實這特規確實有存在的必要。
因為,剛剛我們有看到前端常用的 JSON 格式會有【欄位名稱重複】的情形,像是上述的 EmpNo, LastName 每一筆都會出現一次,當筆數一多時,過多的重複資料會產生不必要的傳輸量,這時採用 DJSON 格式反而會是比較好的選擇。
前端的硬體效能都堪比伺服器,在前端轉換更能有效分配資源。
Delphi 特規 JSON 解析
Table 屬性存放元資料
DJSON 第一個屬性【Table】裡面存放著所有欄位的元資料(MetaData),由左到右依序是:
- Name 欄位名稱
- DataType 主要資料格式
- Ordinal 排位順序編號
- SubType 副資料格式
- Scale 小數位數
- Size 資料長度
- Precision
- ChildPosistion
- Nullable
- Hidden
- ParameterDirection
- ValueParameter
- Literal
如果前端有用上 Grid 之類的元件,這些內容都能夠派上用場。
其餘屬性
其餘屬性就是欄位名稱以及用陣列打包所有的值。
如此一來欄位名稱就只會出現兩次,大部份的場合下內容都會減少許多。
元資料和 JavaScript 的資料格式的相容程度
JavaScript 能接受的資料格式比較少,大致如下:
- String
- Number
- Array
- Null
而元資料格式就是 Delphi 裡大量的格式定義,所以元資料的資料格式主要還是給前端的元件使用,而非給 JavaScript 專用,在把值填入 JavaScript 物件時再讓前端 JS 引擎分配格式即可。
後端工作 - DataSet 轉換為 JSON
這裡我們用上 TDBXJSONTools.TableToJSON 函式進行 JSON 轉換,並在前端直接顯示,效果如下:
中間的圖片是以 TDBXJSONTools.TableToJSON 將 Blob Stream 轉換為 JavaScript Byte Array 後再轉換 Base64 圖像的內容。
一切完美!
看起來很棒,事實上是
原始的圖片大小大約 600KB,JSON 封包傳遞只能傳送文字,圖片轉換為文字後容量通常會變大,但你知道這張圖片大小在轉換後變多大嗎?答案是
2.4MB
容量居然直接放大 4 倍!這到底是怎麼一回事?
親愛的,我把 Byte 陣列變大了
一般來說,我們把圖像 BLOB 轉化為 BYTE Stream 時,就是以 HEX 來表達每一個 Byte,例如:[00112233AABB]。
TDBXJSONTools 也是這樣做的,但它會在每個 byte 中以逗號區隔,例如:[00,11,22,33,AA,BB]。
一個不小心就壯大了起來,但這可不是件好事,我們的目的是要將封包變小,怎麼會把更大的封包拿來傳輸呢!
圖片可以改為Base64文字傳送,但...
由於圖片大小未知,這裡 SQL Server 用上 varchar(max) 來儲存圖片轉化後的 Base64 文字,已知轉化結果可以濃縮為 640KB,這樣傳輸我想就沒有問題了,沒想到轉出來的依然是放大版的 Byte Array,這次容量更衝上 2.8MB 之譜!
神秘的 TDBXDataSetReader
在追查 TDBXJSONTools 原始碼後,發現其 TableToJSON 函式包裝了 TDBXDataSetReader,而它在轉換 TMemoField 時,會認定為 Blob,那還不簡單,強制轉型為 WideMemo 資料格式就可以解決。結果:
簡單的說,被認定的 Blob 造成 WideString 轉不出來,TDBXDataSetReader 裡面到底做了什麼,追了老半天,仍然查不出問題。
究竟該怎麼解決這個問題呢?
<< 未完待續 >>
沒有留言:
張貼留言