关于java:netty系列之netty中的核心解码器json

10次阅读

共计 4039 个字符,预计需要花费 11 分钟才能阅读完成。

简介

程序和程序之间的数据传输方式有很多,能够通过二进制协定来传输,比拟风行的像是 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/

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

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

正文完
 0