本文将介绍物联网领域的JSON格式——CBOR,CBOR是专门为受限制物联网终端设计的数据交换格式,该格式轻量简洁,可以简单理解为二进制形式JSON格式。CBOR格式可以与COAP协议组合使用,犹如HTTP+JSON;另外,CBOR也是COSE的基础。
CBOR可分为8个主类型(Major Type),CBOR格式为了定义8种不同的类型,采用首字节的高3位定义主类型。 首字节的低5位在不同的主类型表示长度(除主类型0和主类型1),如果长度指示不足,则依次使用后续字节。
主类型 | 名称 | 首字节 | 简单说明 |
---|---|---|---|
主类型0 | 无符号整数 | 0x00或 0x10 | 基础类型 |
主类型1 | 负整数 | 0x20或 0x30 | 基础类型 |
主类型2 | 字节数组 | 0x40或 0x50 | 基础类型 |
主类型3 | 字符串 | 0x60或 0x70 | 基础类型 |
主类型4 | 数组 | 0x80或 0x90 | 组合类型,可嵌套任意类型 |
主类型5 | 键值对 | 0xA0或 0xB0 | 组合类型,可嵌套任意类型 |
主类型6 | 扩展 | 0xC0或 0xD0 | 扩展类型 |
主类型7 | 数组 | 0xE0或 0xF0 | 浮点数与简单类型 |
主类型0,无符号整数编码后首字节为0b000_XXXXX。为了表达不同长度的无符号整数,CBOR格式使用第一个字节的低5位表示整数类型
主类型1,无符号整数编码后首字节为0b001_XXXXX。负整数的编码方式与无符号整数相似。
例如:
主类型2,字节数组编码后首字节为0b010_XXXXX。为了表达字节数组长度,如果字符数组的长度小于等于23,那么直接使用首字节的低5位表示;如果长度大于或等于24字节,那么使用第二个字节表示长度;如果长度大于等于256字节,那么使用第二和第三个字节表示长度。
CBOR格式中一般采用多字节组合的方式表达长度。CBOR这样的长度描述方法便于嵌入式设备使用C语言解析CBOR格式,节约宝贵的栈空间与堆空间。
例如:
另外在CBOR格式编码钱的字节数组一般采用采用小写h开头,在单引号中描述HEX格式内容,例如
主类型3。字符串类型编码后首字节为0b011_XXXXX。字符串格式与字节数组格式非常相似,只是字节数组格式人类不可读,而字符格式人类可读。字符串格式表达长度的方式与字节数组类型相似。
例如:
主类型4。 数组编码后首字节为0b100_XXXXX。以上四种均为基础格式,而数组为一种符合,还可以与自身或其他类型嵌套。数组中数组元素个数(不是编码后字节长度)的表达方式与字节数组类型相似。
例如:
在JSON类型中,键名Key必须为字符串,但是在CBOR格式中,键名Key可以是整数。CBOR通过这种方式可以节省物联网终端开销。
主类型5。键值对编码后首字节为0b101_XXXXX。键值对也是一种符合类型,可以嵌套任意类型。键值对类型中键值对个数(不是编码后的字节长度)的表达方式与字节类型表达方式相似。例如
主类型6。扩展类型编码后首字节为0b110_XXXXX。CBOR通过增加Tag的方式扩展类型,满足未来的扩展。COSE规范中通过CBOR Tag定义了多种新类型。本文暂不详细展开扩展类型,仅给出几个CBOR示例
在CBOR扩展类类型描述中,一般以Tag编号开头,然后在小括号中()保存内容,内容可以是任意一种CBOR类型。
主类型7。浮点数与简单类型编码后首字节为0b111_XXXXX。该类型定义了简单类型,时间类型(Date和Time)、大整数(Bignum),10进制整数(Decimal)等。在主类型7中,首字节的高3位固定为0b111,首字节中低5位用于表示子类型。
首字节的低5位中0到23表示简单类,定义如下:
<!-- 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>
@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);
// 通过控制台打印,打印方法省略
}
@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());
// 通过控制台打印,打印方法省略
}
@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));
// 通过控制台打印,打印方法省略
}
/**
* 嵌套数组 [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);
}