简介

程序和程序之间的数据传输方式有很多,能够通过二进制协定来传输,比拟风行的像是thrift协定或者google的protobuf。这些二进制协定能够实现数据的无效传输,并且通过二进制的模式能够节俭数据的体积,在某些速度和效率优先的状况下是十分无效的。并且如果不同的编程语言之间的互相调用,也能够通过这种二进制的协定来实现。

尽管二进制更加疾速和无效,然而对于程序员来说不是很敌对,因为一个人很难间接读取二进制文件,尽管也存在一些一些文本的数据传输方式,比方XML,然而XML的繁琐的标签导致了XML在应用中有诸多的不便。于是一种通用的文本文件传输格局json诞生了。

能读到这篇文章的敌人必定对json不生疏了,当然还有一些更加简洁的文件格式,比方YAML,感兴趣的敌人能够更深刻的理解一下。

这里咱们想要讲的是netty对json的解码。

java中对json的反对

在java中咱们json的应用通常是将一个对象转换成为json进行数据传输,或者将接管到json进行解析,将其转换成为对象。

惋惜的是在JDK中并没有提供给一个好用的JSON工具,所以咱们个别须要借助第三方的JSON包来实现Object和JSON之间的转换工作。

通常应用的有google的GSON,阿里的FastJSON和jackson等。

这里咱们应用google的GSON来进行介绍。

这里咱们次要解说的是java中对象和json的相互转换,所以GSON中其余更加弱小的性能这里就不介绍了。

首先咱们创立一个JAVA对象,咱们定义一个Student类,如下所示:

    static class Student {        String name;        String phone;        Integer age;        public Student(String name, String phone, Integer age) {            this.name = name;            this.phone = phone;            this.age = age;        }    }

这个类中,咱们为Student定义了几个不同的属性和一个构造函数。接下来咱们看下如何应用GSON来对这个对象进行JSON的转换:

        Student obj = new Student("tina","188888888",18);        Gson gson = new Gson();        String json = gson.toJson(obj);        System.out.println(json);        Student 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@4534b60d

netty对json的解码

netty为json提供了一个解码器叫做JsonObjectDecoder,先来看下JsonObjectDecoder的定义:

public class JsonObjectDecoder extends ByteToMessageDecoder

和后面解说的base64,byte数组不同的是,JsonObjectDecoder继承的是ByteToMessageDecoder而不是MessageToMessageDecoder。

这阐明JsonObjectDecoder是间接从ByteBuf转换成为Json Object对象。

咱们晓得JDK中并没有JSON这个对象,所有的对象都是从第三方包中引入的,netty并没有引入新的对象,所以netty中从Json中解析进去的对象还是一个ByteBuf对象,在这个ByteBuf中蕴含了一个Json对象。

JsonObjectDecoder的解析逻辑是怎么样的呢?

首先来看下JsonObjectDecoder中定义的4个state:

    private static final int ST_CORRUPTED = -1;    private static final int ST_INIT = 0;    private static final int ST_DECODING_NORMAL = 1;    private static final int ST_DECODING_ARRAY_STREAM = 2;

ST_INIT示意的是decode的初始状态,ST_CORRUPTED示意的是decode中呈现的异样状态。

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格局呢?

这是因为json中那些特有的分隔符,即便在UTF-8中也是用一个byte来存储的,这样咱们在读取数据的过程中,能够通过读取的byte值和json的分隔符进行比拟,从而来确定json中不同对象的界线。

如果换成其余的编码方式,json中的分隔符可能会用多个byte来示意,这样对咱们的解析就进步了难度,因为咱们须要晓得什么时候是分隔符的开始,什么时候是分隔符的完结。

它的外围解码逻辑如下,首先从ByteBuf中读取一个byte:

byte c = in.getByte(idx);

而后通过调用decodeByte(c, in, idx); 来判断以后的地位是开括号,还是闭括号,是在一个对象的字符串中,还是一个新的对象字符串。

首先须要对以后的state做一个判断,state判断调用的是initDecoding办法:

    private void initDecoding(byte openingBrace) {        openBraces = 1;        if (openingBrace == '[' && streamArrayElements) {            state = ST_DECODING_ARRAY_STREAM;        } else {            state = ST_DECODING_NORMAL;        }    }

接着就是对以后的state和自定义的4个状态进行比拟,如果是一般的json对象,并且对象曾经是闭括号状态,阐明该对象曾经读取实现,能够将其进行转换并输入了:

 if (state == ST_DECODING_NORMAL) {                decodeByte(c, in, idx);                if (openBraces == 0) {                    ByteBuf json = extractObject(ctx, in, in.readerIndex(), idx + 1 - in.readerIndex());                    if (json != null) {                        out.add(json);                    }    ...

如果state示意目前是一个数组对象,数组对象中可能蕴含多个对象,这些对象是通过逗号来辨别的。逗号之间还可能会有空格,所以须要对这些数据进行非凡判断和解决,如下所示:

else if (state == ST_DECODING_ARRAY_STREAM) {                decodeByte(c, in, idx);                if (!insideString && (openBraces == 1 && c == ',' || openBraces == 0 && c == ']')) {                    for (int i = in.readerIndex(); Character.isWhitespace(in.getByte(i)); i++) {                        in.skipBytes(1);                    }                    int idxNoSpaces = idx - 1;                    while (idxNoSpaces >= in.readerIndex() && Character.isWhitespace(in.getByte(idxNoSpaces))) {                        idxNoSpaces--;                    }                    ByteBuf json = extractObject(ctx, in, in.readerIndex(), idxNoSpaces + 1 - in.readerIndex());                    if (json != null) {                        out.add(json);                    }    ....

最初将解析进去的json对象放入byteBuf的out list中,整个解析到此结束。

总结

以上就是netty中json外围解码器JsonObjectDecoder的应用,它的实质是通过判断json对象中的宰割符来宰割多个json字符串,而后将宰割后的json字符串存入ByteBuf中输入。

看到这里,大家可能会纳闷了,decoder不是和encoder一起呈现的吗?为什么netty中只有JsonObjectDecoder,而没有JsonObjectEncoder呢?

事实上,这里的Json对象就是一个蕴含Json字符的字符串,这个字符串被写入到ByteBuf中,所以这里并不需要非凡的encoder。

本文已收录于 http://www.flydean.com/14-3-netty-codec-json/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!