2021/04/16

Delphi 特規 JSON 和 JavaScript Object (1)



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 裡面到底做了什麼,追了老半天,仍然查不出問題。


究竟該怎麼解決這個問題呢?


<< 未完待續 >>

沒有留言:

張貼留言