介绍程序之间的数据传输方式有很多种,可以通过二进制协议进行传输。比较流行的是thriftprotocol或者谷歌的protobuf。这些二进制协议可以实现数据的有效传输,通过二进制的形式可以节省数据量,这在一些优先考虑速度和效率的场合非常有效。而如果不同的编程语言相互调用,也可以通过这种二进制协议来实现。二进制虽然更快更有效,但对程序员来说不是很友好,因为一个人很难直接读取二进制文件。虽然有一些文本数据传输方式,如XML,但是XML繁琐的标签导致XML在使用上存在诸多不便。于是一种通用的文本文件传输格式json诞生了。能看到这篇文章的朋友一定对json不陌生。当然还有一些更简洁的文件格式,比如YAML。有兴趣的朋友可以多了解一下。这里要说的是netty对json的解码。java中对json的支持在java中,我们对json的使用通常是将一个对象转换成json进行数据传输,或者是将接收到的json解析成一个对象。遗憾的是JDK中并没有提供一个好用的JSON工具,所以我们一般需要借助第三方的JSON包来实现Object与JSON的转换。常用的有谷歌的GSON、阿里的FastJSON和jackson等,这里以google的GSON进行介绍。这里主要讲解java和json中对象之间的相互转换,所以GSON中其他更强大的功能就不再介绍了。首先我们创建一个JAVA对象,我们定义一个Student类如下:staticclassStudent{Stringname;字符串电话;整数年龄;publicStudent(Stringname,Stringphone,Integerage){this.name=name;这个.phone=电话;这个。年龄=年龄;在这个类中,我们为Student定义了几个不同的属性和一个构造函数。接下来,让我们看看如何使用GSON将此对象转换为JSON:Studentobj=newStudent("tina","188888888",18);Gsongson=newGson();字符串json=gson.toJson(obj);System.out.println(json);学生obj2=gson.fromJson(json,Student.class);System.out.println(obj2);GSON使用起来非常简单,我们构建好Gson对象后,直接调用toJson方法就可以将对象转成json字符串。然后调用json的fromJson方法将json字符串转为对象。上面代码的输出结果如下:{"name":"tina","phone":"188888888","age":18}com.flydean.JsonTest$Student@4534b60dnetty对json的解码netty为json称为JsonObjectDecoder。我们先看一下JsonObjectDecoder的定义:publicclassJsonObjectDecoderextendsByteToMessageDecoder不同于前面讲解的base64和byte数组。JsonObjectDecoder继承了ByteToMessageDecoder而不是MessageToMessageDecoder。这说明JsonObjectDecoder直接从ByteBuf转换为JsonObject。我们知道JDK中没有JSON对象,所有的对象都是从第三方包中引入的,netty并没有引入新的对象,所以netty中从Json解析出来的对象仍然是一个ByteBuf对象,在这个ByteBuf中包含了一个Json对象。JsonObjectDecoder的解析逻辑是什么?先看JsonObjectDecoder中定义的4种状态:privatestaticfinalintST_CORRUPTED=-1;私人静态最终intST_INIT=0;privatestaticfinalintST_DECODING_NORMAL=1;privatestaticfinalintST_DECODING_ARRAY_STREAM=2;ST_CORRUPTED的初始状态代表解码时出现的异常状态。ST_DECODING_NORMAL代表一个普通的json,如下:{"source":"web","type":"product_info","time":1641967014440,"data":{"id":30000084318055,"staging":false},"dataId":"123456"}ST_DECODING_ARRAY_STREAM表示一个数组。对于数组来说,数组也是一个对象,所以数组也可以用json表示。下面是一个常见的json数组:["Google","Runoob","Taobao"]JsonObjectDecoder的解码逻辑比较简单。主要是读取ByteBuf中的数据,通过判断读取到的数据和json中唯一的大括号、方括号、逗号等分隔符进行分割解析。json对象。需要注意的是,JsonObjectDecoder解码的ByteBuf中的消息应该是UTF-8编码格式。为什么需要UTF-8格式?这是因为即使在UTF-8中json中的唯一分隔符也是存储在一个字节中的,这样我们在读取数据的过程中就可以将读取到的字节值与json的分隔符进行比较。为了判断json中不同对象的边界。如果换成其他编码方式,json中的分隔符可能会用多个字节来表示,这就增加了我们分析的难度,因为我们需要知道分隔符什么时候开始,什么时候分隔符结束。其核心解码逻辑如下,首先从ByteBuf中读取一个字节:bytec=in.getByte(idx);然后调用decodeByte(c,in,idx);确定当前位置是左括号还是右括号,是在一个对象字符串中,还是一个新的对象字符串。首先,你需要对当前的状态做出判断。状态判断调用initDecoding方法:privatevoidinitDecoding(byteopeningBrace){openBraces=1;if(openingBrace=='['&&streamArrayElements){state=ST_DECODING_ARRAY_STREAM;}else{state=ST_DECODING_NORMAL;下一步是将当前状态与4个自定义状态进行比较。如果是一个普通的json对象,并且对象已经处于右括号状态,则表示该对象已经被读取,可以进行转换输出:if(state==ST_DECODING_NORMAL){decodeByte(c,in,idx);if(openBraces==0){ByteBufjson=extractObject(ctx,in,in.readerIndex(),idx+1-in.readerIndex());如果(json!=null){out.add(json);}...如果state表示当前是数组对象,则数组对象可能包含多个对象,对象之间用逗号隔开。逗号之间可能有空格,所以需要对这些数据进行特殊的判断和处理,如下:elseif(state==ST_DECODING_ARRAY_STREAM){decodeByte(c,in,idx);if(!insideString&&(openBraces==1&&c==','||openBraces==0&&c==']')){for(inti=in.readerIndex();Character.isWhitespace(in.getByte(i));i++){in.skipBytes(1);}intidxNoSpaces=idx-1;while(idxNoSpaces>=in.readerIndex()&&Character.isWhitespace(in.getByte(idxNoSpaces))){idxNoSpaces--;}ByteBufjson=extractObject(ctx,in,in.readerIndex(),idxNoSpaces+1-in.readerIndex());如果(json!=null){out.add(json);}....最后把解析出来的json对象放到byteBuf的out列表中,到这里整个解析就结束了。综上所述,以上就是netty中json核心解码器JsonObjectDecoder的使用。它的本质是通过判断json对象中的分隔符,将多个json串拆分,然后将拆分后的json串存放到ByteBuf中输出。看到这里,你可能会疑惑,解码器不是和编码器一起出现的吗?为什么netty中只有JsonObjectDecoder,没有JsonObjectEncoder?其实这里的Json对象就是一个包含Json字符的字符串,而这个字符串是写入到ByteBuf中的,所以这里不需要特殊的编码器。本文已收录于http://www.flydean.com/14-3-netty-codec-json/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!
