2025年5月14日 星期三

Streamlit 學習筆記 : 顯示 JSON 資料

本篇旨在測試 Streamlit 輸出 JSON 資料的方法. JSON 源自 Javascript 物件, 因其輕量級的資料表示格式受到跨平台與跨語言支持, 已取代 XML 成為網路中最受歡迎與普遍接受的前後端資料交換格式, 其副檔名為 .json. 

JSON 支援兩種資料格式 :
  1. 物件 :
    以大括號 {} 包住的鍵值對 (key-value pairs), 鍵必須是字串, 值可以是數值, 字串, 布林值 (true/false), 空值 (null), 陣列, 或其他物件. 不論鍵或值的字串必須用雙引號括起來, 不可使用單引號. 
  2. 陣列 : 
    以 [] 括起來的數值, 字串, 布林值 (true/false), 空值 (null), 陣列, 或其他物件.
JSON 的物件格式類似 Python 的字典 (但布林值與空值表示法不同), 陣列格式則相當於 Python 的串列. 最常用的是物件格式, Python 的內建模組 json 提供字典與 JSON 字串之間互轉的函式 : 
  • json.loads() : 傳入 JSON 字串, 傳回 Python 字典
  • json.dumps() : 傳入 Python 字典, 傳回 JSON 字串
參考 :


例如 :

>>> import json   
>>> json_str='{"姓名": "金智媛", "性別": "女", "年齡": 32, "身高": 172.5, "已婚": false, "宗教": null}'   
>>> obj=json.loads(json_str)    
>>> type(obj)   
<class 'dict'>   
>>> obj   
{'姓名': '金智媛', '性別': '女', '年齡': 32, '身高': 172.5, '已婚': False, '宗教': None}

可見 loads() 會將 Javascript 的 true/false 轉成 Python 的 True/False; 將 Javascript 的 null 轉成 Python 的 None. 

將此 obj 字典傳給 json.dumps() 則會轉回 JSON 字串 :

>>> json.dumps(obj)   
'{"\\u59d3\\u540d": "\\u91d1\\u667a\\u5a9b", "\\u6027\\u5225": "\\u5973", "\\u5e74\\u9f61": 32, "\\u8eab\\u9ad8": 172.5, "\\u5df2\\u5a5a": false, "\\u5b97\\u6559": null}'

可見非 ASCII 字元都被轉成 Unicode 表示了, 這是因為 json.dumps() 為了確保跨平台兼容性, 預設會把中文轉成 Unicode 的跳脫序列 (\uXXXX), 如果要顯示為中文就不要跳脫, 可傳入 ensure_ascii=False 參數值 (預設 True) 來設定 :

>>> json.dumps(obj, ensure_ascii=False)   
'{"姓名": "金智媛", "性別": "女", "年齡": 32, "身高": 172.5, "已婚": false, "宗教": null}'

注意, JSON 要求所有字串 (key 和 value) 都必須用雙引號, 用單引號是不合語法的, 呼叫 json.loads() 時會拋出例外, 例如我們將上面的 JSON 字串改成鍵值都用單引號 : 

>>> json_str="{'姓名': '金智媛', '性別': '女', '年齡': 32, '身高': 172.5, '已婚': false, '宗教': null}"  
>>> obj=json.loads(json_str)   
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\tony1\AppData\Local\Programs\Thonny\lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "C:\Users\tony1\AppData\Local\Programs\Thonny\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Users\tony1\AppData\Local\Programs\Thonny\lib\json\decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

可見用單引號會出現 JSONDecodeError 例外. 

Streamlit 提供了 json() 函式來顯示 JSON 資料 :

st.json(body, expanded=True)  

參數 body 為要顯示的 JSON 內容 (必要), 可以是 dict, list, 或合法的 JSON 字串; expanded 參數用來設定是否要展開 JSON 樹狀結構 (預設為展開), 例如顯示上面的 json_str 字串 : 


測試 1 : 顯示 JSON 字串與 Python 字典 [看原始碼]

# st-json-test-1.py
import json
import streamlit as st

st.subheader("顯示 JSON 字串")
json_str='{"姓名": "金智媛", "性別": "女", "年齡": 32, "身高": 172.5, "已婚": false, "宗教": null}'
st.json(json_str)
obj=json.loads(json_str)
st.subheader("顯示 Python 字典")
st.json(obj, expanded=False)

此例呼叫兩次 json() 函式, 第一次是顯示 JSON 字串; 第二次是顯示用 json.loads() 轉成的 Python 字典 (設為布展開), 執行結果如下 :



可見 Python 字典因為傳入 expanded=False 是收合的, 點左方的小三角按鈕可展開, 滑鼠移到每一個鍵旁邊會出現一個複製按鈕, 可將該鍵的值甚至整個 JSON 資料複製到剪貼簿 : 




可見不論是 JSON 字串還是用 json.loads() 轉換成 Python 字典, st.json() 輸出的結果都是一樣的 (Python 字典的 True/False 與 None 會自動轉換為 true/false 與 NULL). 

下面範例是用 st.json() 顯示陣列形式的 JSON 資料 (用 Python 串列) :


測試 2 : 顯示 Python 串列 [看原始碼]

# st-json-test-2.py
import json
import streamlit as st

st.subheader('顯示 Python 串列')
data=['台積電', '中碳', '台泥', '玉山金']
st.json(data)

結果如下 : 



可見每一個元素前面都會顯示其索引. 

除了 st.json() 外, 當然也可以用超級函式 write() 來輸出 JSON 資料, 例如 :


測試 3 : 呼叫 write() 顯示 JSON 資料 [看原始碼]

# st-json-test-3.py
import json
import streamlit as st

st.subheader("顯示 JSON 字串")
json_str='{"姓名": "金智媛", "性別": "女", "年齡": 32, "身高": 172.5, "已婚": false, "宗教": null}'
st.write(json_str)

obj=json.loads(json_str)
st.subheader("顯示 Python 字典")
st.write(obj, expanded=False)

st.subheader('顯示 Python 串列')
data=['台積電', '中碳', '台泥', '玉山金']
st.write(data)

結果如下 : 




可見 st.write() 只能於傳入 Python 字典時像 st.json() 那樣顯示 JSON 樹狀結構, 如果傳入 JSON 字串它就直接輸出字串, 所以 JSON 字串必須傳給 st.json() 顯示為宜. 

沒有留言 :