在上一篇文章中,我們第一次完整閱讀了一份Patient Resource,也看到了大量的大括號、中括號、冒號和逗號。
例如:
{
"resourceType": "Patient",
"id": "patient-example",
"active": true
}
這種資料格式就是JSON。
FHIR並不是只能使用JSON,也可以使用XML等格式表達Resource。不過,JSON在Web API中非常常見,結構相對精簡,也方便搭配Postman及各種程式語言處理,因此本系列會以JSON作為主要示範格式。
今天先暫時放下複雜的FHIR欄位,專門認識JSON的基本語法,學會分辨Key、Value、Object、Array及常見資料型別。
JSON的全名是:
JavaScript Object Notation
中文通常稱為JavaScript物件表示法。
雖然名稱中有JavaScript,但JSON不是只能在JavaScript中使用。它是一種輕量、文字形式,且不依賴特定程式語言的資料交換格式。
許多程式語言都能讀取及產生JSON,例如:
JSON經常被應用在:
它適合用來表達有結構的資料,也相對容易讓人閱讀。
FHIR並不只支援JSON。
常見的FHIR資料格式包括:
同一筆Patient Resource可以使用不同格式呈現。
{
"resourceType": "Patient",
"id": "patient-001",
"gender": "male"
}
<Patient xmlns="http://hl7.org/fhir">
<id value="patient-001"/>
<gender value="male"/>
</Patient>
兩者表達的是相似資料,只是語法不同。
JSON與現代Web API的使用方式較接近,內容也通常比XML精簡,因此FHIR API經常使用JSON進行交換。
當系統傳送FHIR JSON時,常見的Content-Type是:
application/fhir+json
這是在告訴接收方:「這次傳送的內容是FHIR JSON資料。」
先看一份最簡單的病人資料:
{
"name": "王小明",
"gender": "male"
}
這份JSON由幾個基本符號組成:
| 符號 | 用途 |
|---|---|
{ } |
表示Object |
[ ] |
表示Array |
: |
分隔Key與Value |
, |
分隔不同資料項目 |
" " |
包住字串及Key |
接下來逐一介紹。
JSON中的資料通常由Key與Value組成。
例如:
"name": "王小明"
其中:
"name"是Key"王小明"是Value:分隔可以把它想成表單上的欄位名稱和填寫內容:
| Key | Value |
|---|---|
| name | 王小明 |
| gender | male |
| birthDate | 2000-01-01 |
將它們寫成JSON就是:
{
"name": "王小明",
"gender": "male",
"birthDate": "2000-01-01"
}
在JSON中,Key必須是字串,因此需要使用雙引號包住。
正確寫法:
"name": "王小明"
錯誤寫法:
name: "王小明"
Object使用大括號表示:
{
"family": "王",
"given": "小明"
}
Object中可以放入多組Key與Value,每一組之間使用逗號分隔。
上面的Object包含兩筆資料:
family的值是王
given的值是小明
在FHIR中,許多欄位的Value本身也是Object。
例如,Patient的姓名可以寫成:
{
"resourceType": "Patient",
"name": {
"family": "王",
"given": "小明"
}
}
這裡最外層是一個Object,而name的Value又是另一個Object。這種結構可以稱為巢狀Object。
不過,上面只是用來理解JSON的簡化示範。FHIR R4中的Patient.name實際上是可以重複的欄位,因此正式JSON需要使用Array表示。
Array使用中括號表示:
[
"王小明",
"王大明"
]
Array中可以放入多個值,每個值之間使用逗號分隔。
在FHIR Patient中,name是一個可以重複的欄位,因為同一個人可能同時具有正式姓名、舊名或暱稱。
因此,FHIR中的name會使用Array:
{
"name": [
{
"use": "official",
"text": "王小明"
},
{
"use": "nickname",
"text": "小明"
}
]
}
這個name Array中包含兩個Object:
可以直接看外面的括號:
{
"key": "value"
}
使用大括號{ },內容通常是Key與Value的組合。
[
"value1",
"value2"
]
使用中括號[ ],內容是一組依照順序排列的值。
Array也可以放入多個Object:
[
{
"system": "phone",
"value": "0900-000-001"
},
{
"system": "email",
"value": "patient@example.com"
}
]
以下是一份簡化的Patient Resource:
{
"resourceType": "Patient",
"id": "patient-example",
"identifier": [
{
"system": "https://hospital.example.org/mrn",
"value": "MRN0001"
}
],
"name": [
{
"family": "王",
"given": [
"小明"
]
}
],
"active": true
}
可以從最外層開始閱讀。
最外面使用:
{
...
}
所以整份Patient Resource是一個Object。
"identifier": [
...
]
identifier後面使用中括號,所以它的Value是一個Array。
{
"system": "https://hospital.example.org/mrn",
"value": "MRN0001"
}
identifier Array裡面放入一個Object。
"name": [
{
"family": "王",
"given": [
"小明"
]
}
]
name是Array,裡面放入一個姓名Object。
"given": [
"小明"
]
given也是Array,只是這次Array裡放的是字串,而不是Object。
FHIR JSON中經常出現Object包住Array、Array又包含Object的結構。閱讀時不需要一次看完整份資料,可以從最外層開始,一層一層往內拆解。
JSON的Value不一定都是文字。
標準JSON可以表達以下幾種資料型別:
| 資料型別 | 範例 |
|---|---|
| String | "王小明" |
| Number | 37.5 |
| Boolean | true |
| Null | null |
| Object | { "family": "王" } |
| Array | ["小明"] |
String用來表示文字,必須使用雙引號包住。
"name": "王小明"
"birthDate": "2000-01-01"
雖然出生日期看起來和一般文字不同,但在JSON中仍然以字串表示。
FHIR會再針對欄位規定更明確的資料型別和格式,例如birthDate必須符合FHIR date的規則。
正確:
"gender": "male"
錯誤:
"gender": male
如果沒有雙引號,JSON會將male當成其他語法,而不是文字。
Number不需要使用雙引號。
"value": 37.5
如果將數字放進雙引號:
"value": "37.5"
它就會變成String,而不是Number。
兩者看起來很像,但資料型別不同:
| JSON | 資料型別 |
|---|---|
37.5 |
Number |
"37.5" |
String |
在FHIR中,欄位應使用哪一種資料型別會由規範決定,不能因為看起來相同就任意交換。
Boolean只有兩個值:
true
false
例如:
"active": true
代表這筆Patient紀錄目前有效。
Boolean必須使用小寫,而且不需要雙引號。
正確:
"active": true
錯誤:
"active": "true"
上面的"true"是String。
錯誤:
"active": True
JSON中的Boolean不能寫成大寫開頭的True。
標準JSON可以使用:
null
表示沒有值。
例如一般JSON可能出現:
{
"middleName": null
}
不過,FHIR JSON對空值有自己的規則。FHIR欄位通常不應直接使用null,如果某個欄位沒有內容,一般會直接省略該欄位。
例如,不知道病人的電話時,通常不是寫:
"telecom": null
而是不要放入telecom:
{
"resourceType": "Patient",
"id": "patient-example"
}
因此,要分清楚「JSON語法允許什麼」與「FHIR JSON規範允許什麼」。符合一般JSON語法,不一定代表符合FHIR。
JSON的Object和Array可以互相組合,形成多層結構。
例如:
{
"resourceType": "Patient",
"address": [
{
"use": "home",
"text": "桃園市中壢區範例路100號"
}
]
}
可以拆成:
address的Value是Array。use及text。這種一層包住另一層的形式,就是巢狀結構。
當FHIR Resource很長時,可以利用縮排觀察資料層級。
下面兩段JSON表達相同資料。
{
"resourceType": "Patient",
"id": "patient-example",
"active": true
}
{"resourceType":"Patient","id":"patient-example","active":true}
空格、縮排和換行主要是為了方便人類閱讀,通常不會改變JSON資料的意義。
不過,在學習或除錯時,我會建議保留整齊縮排。當括號很多時,比較容易看出每一層Object和Array的範圍。
在JSON Object中,欄位順序通常不影響資料意義。
下面兩份資料表達相同內容:
{
"id": "patient-example",
"active": true
}
{
"active": true,
"id": "patient-example"
}
但是,Array中的順序會被保留。
例如:
"given": [
"小明",
"大明"
]
第一個值與第二個值的位置仍然具有順序。
FHIR官方也說明,JSON Object屬性的順序沒有意義,但Array元素的順序需要保留。
JSON中的Key區分英文大小寫。
例如:
"resourceType": "Patient"
不能任意改成:
"resourcetype": "Patient"
也不能寫成:
"ResourceType": "Patient"
這三個Key對電腦來說並不相同。
FHIR已經定義好每個欄位的正式名稱,因此必須依照規範使用正確的英文大小寫。
錯誤:
{
'resourceType': 'Patient'
}
標準JSON的Key及String應使用雙引號。
正確:
{
"resourceType": "Patient"
}
部分程式語言可能接受單引號,但那不代表它是符合標準的JSON。
錯誤:
{
"resourceType": "Patient"
"id": "patient-example"
}
resourceType和id是兩組資料,中間需要使用逗號分隔。
正確:
{
"resourceType": "Patient",
"id": "patient-example"
}
錯誤:
{
"resourceType": "Patient",
"id": "patient-example",
}
最後一筆資料後面不能再放逗號。
正確:
{
"resourceType": "Patient",
"id": "patient-example"
}
錯誤:
{
"name": [
{
"text": "王小明"
}
}
上面的name Array少了一個右中括號]。
正確:
{
"name": [
{
"text": "王小明"
}
]
}
當JSON有多層巢狀結構時,縮排可以協助我們檢查每個括號是否成對。
許多程式語言可以使用註解,但標準JSON本身不支援以下寫法:
{
// 這是病人資料
"resourceType": "Patient"
}
也不支援:
{
"resourceType": "Patient" /* Resource類型 */
}
如果要說明欄位,應該寫在文章或程式文件中,不要直接把註解放入要傳送的JSON資料。
不一定。
下面是一份語法正確的JSON:
{
"resourceType": "Patient",
"favoriteFood": "蛋糕"
}
它可以被JSON工具正常讀取,但FHIR R4 Patient並沒有favoriteFood這個標準欄位。
另一個例子:
{
"resourceType": "Patient",
"active": "true"
}
這也是合法JSON,但active在FHIR中應該是Boolean,而不是String。
正確寫法是:
{
"resourceType": "Patient",
"active": true
}
因此,FHIR資料至少需要經過兩個層次的檢查:
面對一大段FHIR JSON時,可以使用以下方法:
"resourceType": "Patient"
先確認這是哪一種Resource。
縮排越深,通常代表資料位於越內層的Object或Array。
{ }是Object。[ ]是Array。不要一開始就想看懂整份Resource。可以先讀id、name及birthDate,再慢慢理解其他欄位。
Postman、程式編輯器及許多JSON工具都可以自動排版,讓巢狀結構更清楚。
但如果資料中包含真實病人資訊,不應任意貼到不確定安全性的公開網站進行格式化或驗證。
請先觀察下面這份FHIR JSON:
{
"resourceType": "Patient",
"id": "patient-002",
"active": true,
"name": [
{
"use": "official",
"family": "陳",
"given": [
"小華"
]
}
],
"telecom": [
{
"system": "email",
"value": "patient@example.com"
}
]
}
可以找出:
resourceType的Value是String。active的Value是Boolean。name的Value是Array。name Array中包含一個Object。given仍然是一個Array。telecom Array中包含一筆電子郵件資料。只要能夠分辨這些層次,就已經具備閱讀FHIR JSON的重要基礎。
今天認識了JSON的基本結構,包括:
也整理了幾個常見錯誤:
FHIR雖然可以使用JSON表示,但符合JSON語法不代表一定符合FHIR規範。JSON只負責資料的基本表示方式,FHIR還會進一步規定欄位名稱、資料型別、代碼及出現次數。
下一篇將延續今天的內容,進一步認識FHIR自己的常見資料型別,了解為什麼姓名、地址、識別碼和醫療代碼不是單純的文字欄位。
Day 10|FHIR常見資料型別一次看懂
RFC 8259:The JavaScript Object Notation(JSON)Data Interchange Format
https://www.rfc-editor.org/rfc/rfc8259
HL7 FHIR R4:JSON Format
https://hl7.org/fhir/R4/json.html
HL7 FHIR R4:Data Types
https://hl7.org/fhir/R4/datatypes.html
HL7 FHIR R4:Patient Resource
https://hl7.org/fhir/R4/patient.html