前言
本文將介紹物聯(lián)網(wǎng)領(lǐng)域的JSON格式——CBOR辛孵,CBOR是專門為受限制物聯(lián)網(wǎng)終端設(shè)計的數(shù)據(jù)交換格式,該格式輕量間接赡磅,可以簡單理解為二進制形式JSON格式魄缚。CBOR格式可以與COAP協(xié)議組合使用,猶如HTTP+JSON仆邓;另外鲜滩,CBOR也是COSE的基礎(chǔ)伴鳖。
CBOR簡述
CBOR可分為8個主類型(Major Type),CBOR格式為了定義8種不同的類型徙硅,采用首字節(jié)的高3位定義主類型榜聂。 首字節(jié)的低5位在不同的主類型表示長度(除主類型0和主類型1),如果長度指示不足嗓蘑,則依次使用后續(xù)字節(jié)须肆。
主類型 | 名稱 | 首字節(jié) | 簡單說明 |
---|---|---|---|
主類型0 | 無符號整數(shù) | 0x00或 0x10 | 基礎(chǔ)類型 |
主類型1 | 負整數(shù) | 0x20或 0x30 | 基礎(chǔ)類型 |
主類型2 | 字節(jié)數(shù)組 | 0x40或 0x50 | 基礎(chǔ)類型 |
主類型3 | 字符串 | 0x60或 0x70 | 基礎(chǔ)類型 |
主類型4 | 數(shù)組 | 0x80或 0x90 | 組合類型,可嵌套任意類型 |
主類型5 | 鍵值對 | 0xA0或 0xB0 | 組合類型桩皿,可嵌套任意類型 |
主類型6 | 擴展 | 0xC0或 0xD0 | 擴展類型 |
主類型7 | 數(shù)組 | 0xE0或 0xF0 | 浮點數(shù)與簡單類型 |
無符號整數(shù) an unsigned integer
主類型0豌汇,無符號整數(shù)編碼后首字節(jié)為0b000_XXXXX。為了表達不同長度的無符號整數(shù)泄隔,CBOR格式使用第一個字節(jié)的低5位表示整數(shù)類型
- 0b000_11000 uint8_t
- 0b000_11001 uint16_t
- 0b000_11010 uint32_t
- 0b000_11011 uint64_t
請注意拒贱,無符號整數(shù)0到23直接表達,無需使用整數(shù)類型佛嬉。
例如: - 10 編碼后 0x0A
- 24 編碼后 0x1818
- 100 編碼后 0x1864
- 1000 編碼后 0x1903E8
負整數(shù) a negative integer
主類型1逻澳,無符號整數(shù)編碼后首字節(jié)為0b001_XXXXX。負整數(shù)的編碼方式與無符號整數(shù)相似暖呕。
例如:
- -10 編碼后 0x29
- -24 編碼后 0x37
- -100 編碼后 0x3863
- -1000 編碼后 0x3903E7
字節(jié)數(shù)組 a byte string
主類型2斜做,字節(jié)數(shù)組編碼后首字節(jié)為0b010_XXXXX。為了表達字節(jié)數(shù)組長度湾揽,如果字符數(shù)組的長度小于等于23瓤逼,那么直接使用首字節(jié)的低5位表示;如果長度大于或等于24字節(jié)库物,那么使用第二個字節(jié)表示長度霸旗;如果長度大于等于256字節(jié),那么使用第二和第三個字節(jié)表示長度艳狐。
CBOR格式中一般采用多字節(jié)組合的方式表達長度定硝。CBOR這樣的長度描述方法便于嵌入式設(shè)備使用C語言解析CBOR格式,節(jié)約寶貴的椇聊浚空間與堆空間蔬啡。
例如:
- HEX格式01020304 編碼后 0x4401020304
- 長度為23的字節(jié)數(shù)組 編碼后 0x57XX....
- 長度為24的字節(jié)數(shù)組 編碼后 0x5818XX...
- 長度為100的字節(jié)數(shù)組 編碼后 0x5901F4XX...
本質(zhì)來說,CBOR僅為這些原始的字節(jié)數(shù)組增加了一個長度描述镀虐。
特別注意點
另外在CBOR格式編碼錢的字節(jié)數(shù)組一般采用采用小寫h開頭箱蟆,在單引號中描述HEX格式內(nèi)容,例如
- h'01020304'
字符串 a text string
主類型3刮便。字符串類型編碼后首字節(jié)為0b011_XXXXX空猜。字符串格式與字節(jié)數(shù)組格式非常相似,只是字節(jié)數(shù)組格式人類不可讀,而字符格式人類可讀辈毯。字符串格式表達長度的方式與字節(jié)數(shù)組類型相似坝疼。
例如:
- "a" 編碼后 0x6161
- "IETF" 編碼后 0x6449455446
- 長度為24的字符串 編碼后 0x781830XX...
數(shù)組 an array of data items
主類型4。 數(shù)組編碼后首字節(jié)為0b100_XXXXX谆沃。以上四種均為基礎(chǔ)格式钝凶,而數(shù)組為一種符合,還可以與自身或其他類型嵌套唁影。數(shù)組中數(shù)組元素個數(shù)(不是編碼后字節(jié)長度)的表達方式與字節(jié)數(shù)組類型相似耕陷。
例如:
- [1,2,3] 編碼后 0x83010203
- [1,[2,3], [4,5]] 編碼后 0x8301820203820405,此處包括3個數(shù)組据沈,第一個數(shù)組0x83哟沫,表示元素個數(shù)為3,第二個0x82b表示元素個數(shù)為2锌介,第3個編碼后元素個數(shù)為3嗜诀。
對于數(shù)組部分,RFC7049也有些表述不清的地方孔祸。在主類型無符號整數(shù)中裹虫,若整數(shù)值超過24(0x18),該值將會被CBOR編碼為0x1818融击,所以 - [24, 25, 26] 編碼后為 0x8318181819181A,不是0x83181818雳窟。
- [500, 501, 502] 編碼后為0x831901F41901F51901F6尊浪,不是0x8301F401F501F6
特別注意點
在JSON類型中,鍵名Key必須為字符串封救,但是在CBOR格式中拇涤,鍵名Key可以是整數(shù)。CBOR通過這種方式可以節(jié)省物聯(lián)網(wǎng)終端開銷誉结。
鍵值對 a map of pairs of data items
主類型5鹅士。鍵值對編碼后首字節(jié)為0b101_XXXXX。鍵值對也是一種符合類型惩坑,可以嵌套任意類型掉盅。鍵值對類型中鍵值對個數(shù)(不是編碼后的字節(jié)長度)的表達方式與字節(jié)類型表達方式相似。例如
- {"a":1, "b":[2,3]} 編碼后 0xA26161016162820203以舒, 其中0x616101中 0x616101表示一個鍵值對趾痘,0x6161表示字符串編碼"a", 0x01表示值1蔓钟。其中0x6162820203表示另一個鍵值對永票,0x6162表示字符串編碼"b",0x820203表示一個數(shù)組。
- {1:2, 3:4} 編碼后 0xA201020304 (還需要分析,JSON中鍵名不能為數(shù)字侣集,而CBOR可以)
擴展類型
主類型6键俱。擴展類型編碼后首字節(jié)為0b110_XXXXX。CBOR通過增加Tag的方式擴展類型世分,滿足未來的擴展编振。COSE規(guī)范中通過CBOR Tag定義了多種新類型。本文暫不詳細展開擴展類型罚攀,僅給出幾個CBOR示例
- 23(h'01020304') 編碼后 0xd74401020304
特別說明
在CBOR擴展類類型描述中党觅,一般以Tag編號開頭,然后在小括號中()保存內(nèi)容斋泄,內(nèi)容可以是任意一種CBOR類型杯瞻。
浮點數(shù)與簡單類型
主類型7。浮點數(shù)與簡單類型編碼后首字節(jié)為0b111_XXXXX炫掐。該類型定義了簡單類型魁莉,時間類型(Date和Time)、大整數(shù)(Bignum)募胃,10進制整數(shù)(Decimal)等旗唁。在主類型7中,首字節(jié)的高3位固定為0b111痹束,首字節(jié)中低5位用于表示子類型检疫。
簡單類型
首字節(jié)的低5位中0到23表示簡單類,定義如下:
- 20 表達False
- 21 表達True
- 22 表達Null
- 23 表達Undefined Value
所以 - False 編碼后 0xF4
- True 編碼后 0xF5
- Null 編碼后 0xF6
時間類型
CBOR體驗
參考依賴
<!-- https://mvnrepository.com/artifact/com.upokecenter/cbor -->
<dependency>
<groupId>com.upokecenter</groupId>
<artifactId>cbor</artifactId>
<version>4.0.0-alpha2</version>
</dependency>
還依賴了兩個參考庫joda-time
和hexdump
<dependency>
<groupId>org.lasinger.tools</groupId>
<artifactId>hexdump</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.2</version>
</dependency>
整數(shù)相關(guān)
@Test
public void testInt() {
CBORObject obj = CBORObject.FromObject(1);
// 通過控制臺打印
byte[] bytes = obj.EncodeToBytes();
String hexString = Hexdump.hexdump(bytes);
System.out.println(hexString);
}
@Test
public void testInt100() {
CBORObject obj = CBORObject.FromObject(100);
// 通過控制臺打印祷嘶,打印方法省略
}
@Test
public void testIntNegative100() {
CBORObject obj = CBORObject.FromObject(-100);
// 通過控制臺打印屎媳,打印方法省略
}
字節(jié)數(shù)組與字符串
@Test
public void testByteArray() {
int length = 500;
byte[] testByte = new byte[length];
for (int i = 0; i < length; i++) {
testByte[i] = 0x30;
}
CBORObject obj = CBORObject.FromObject(testByte);
// 通過控制臺打印,打印方法省略
}
@Test
public void testString() {
CBORObject obj = CBORObject.FromObject("IETF");
// 通過控制臺打印论巍,打印方法省略
}
@Test
public void testLargeString() {
int length = 24;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append("0");
}
CBORObject obj = CBORObject.FromObject(builder.toString());
// 通過控制臺打印烛谊,打印方法省略
}
數(shù)組
@Test
public void testArray() {
CBORObject obj = CBORObject.NewArray();
obj.Add(CBORObject.FromObject(1));
obj.Add(CBORObject.FromObject(2));
obj.Add(CBORObject.FromObject(3));
// 通過控制臺打印,打印方法省略
}
@Test
public void testArray24() {
CBORObject obj = CBORObject.NewArray();
obj.Add(CBORObject.FromObject(500));
obj.Add(CBORObject.FromObject(501));
obj.Add(CBORObject.FromObject(502));
// 通過控制臺打印嘉汰,打印方法省略
}
/**
* 嵌套數(shù)組 [1, [2,3], [4,5]]
*/
@Test
public void testMultiArray() {
CBORObject obj = CBORObject.NewArray();
obj.Add(CBORObject.FromObject(1));
CBORObject subArray1 = CBORObject.NewArray();
subArray1.Add(CBORObject.FromObject(2));
subArray1.Add(CBORObject.FromObject(3));
obj.Add(subArray1);
CBORObject subArray2 = CBORObject.NewArray();
subArray2.Add(CBORObject.FromObject(4));
subArray2.Add(CBORObject.FromObject(5));
obj.Add(subArray2);
// 通過控制臺打印丹禀,打印方法省略
}
@Test
public void testLargeArray() {
CBORObject obj = CBORObject.NewArray();
int length = 25;
for (int i = 0; i < length; i++) {
int temp = i + 100;
obj.Add(CBORObject.FromObject(temp));
}
// 通過控制臺打印,打印方法省略
}
鍵值對
@Test
public void testMap() {
CBORObject obj = CBORObject.NewMap();
obj.set(1, CBORObject.FromObject(2));
obj.set(3, CBORObject.FromObject(4));
// 通過控制臺打印鞋怀,打印方法省略
}
@Test
public void testJavaMap() {
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
CBORObject obj = CBORObject.FromObject(map);
// 通過控制臺打印双泪,打印方法省略
}
浮點型和簡單類型
@Test
public void testTrue() {
CBORObject obj = CBORObject.FromObject(true);
byte[] bytes = obj.EncodeToBytes();
String hexString = Hexdump.hexdump(bytes);
System.out.println(hexString);
}
@Test
public void testBigDecimal() {
String decimalString = BigDecimal.valueOf(273.15).toString();
CBORObject obj = CBORObject.FromObject(EDecimal.FromString(decimalString));
// 通過控制臺打印,打印方法省略
}
@Test
public void testDateTime() {
DateTime dt = new DateTime(2013, 3, 21, 20, 04, 0);
CBORObject obj = CBORObject.FromObject(dt.toDate());
// 通過控制臺打印密似,打印方法省略
}
擴展類型
@Test
public void testCBORTag() {
byte[] array = new byte[] {0x01, 0x02, 0x03, 0x04};
CBORObject obj = CBORObject.FromObjectAndTag(array, 23);
System.out.println(obj.toString());
byte[] bytes = obj.EncodeToBytes();
String hexString = Hexdump.hexdump(bytes);
System.out.println(hexString);
}
總結(jié)
- CBOR格式是一種帶有明顯長度指示的傳輸協(xié)議攒读,而常用的JSON格式并沒有長度指示。長度指示可以幫助終端設(shè)備在進行CBOR解析時節(jié)約寶貴的堆空間辛友。
- CBOR格式支持鍵值對形式 Key-Value薄扁,Key可以是整數(shù)剪返,而JSON格式中Key值只能是字符串。
- CBOR格式中Date邓梅、Time脱盲、Decimal類型解決了物聯(lián)網(wǎng)終端設(shè)備中時間日期與十進制數(shù)表達的問題。