简介
在 json 之前,xml 是最罕用的数据传输格局,尽管 xml 的冗余数据有点多,然而 xml 的构造简略清晰,至今依然使用在程序中的不同中央,对于 netty 来说天然也提供了对于 xml 数据的反对。
netty 对 xml 的反对体现在两个方面,第一个方面是将编码过后的多个 xml 数据进行 frame 拆分,每个 frame 蕴含一个残缺的 xml。另一方面是将宰割好的 frame 进行 xml 的语义解析。
进行 frame 拆分能够应用 XmlFrameDecoder, 进行 xml 文件内容的解析则能够应用 XmlDecoder,接下来咱们会具体解说两个 decoder 实现和应用。
XmlFrameDecoder
因为咱们收到的是数据流,所以不确定收到的数据到底是什么样的,一个失常的 xml 数据可能会被拆分成多个数据 frame。
如下所示:
+-------+-----+--------------+
| <this | IsA | XMLElement/> |
+-------+-----+--------------+
这是一个失常的 xml 数据,然而被拆分成为了三个 frame,所以咱们须要将其合并成为一个 frame 如下:
+-----------------+
| <thisIsAXMLElement/> |
+-----------------+
还有可能不同的 xml 数据被分拆在多个 frame 中的状况, 如下所示:
+-----+-----+-----------+-----+----------------------------------+
| <an | Xml | Element/> | <ro | ot><child>content</child></root> |
+-----+-----+-----------+-----+----------------------------------+
下面的数据须要拆分成为两个 frame:
+-----------------+-------------------------------------+
| <anXmlElement/> | <root><child>content</child></root> |
+-----------------+-------------------------------------+
拆分的逻辑很简略,次要是通过判断 xml 的分隔符的地位来判断 xml 是否开始或者完结。xml 中的分隔符有三个,别离是 '<‘, ‘>’ 和 ‘/’。
在 decode 办法中只须要判断这三个分隔符即可。
另外还有一些额定的判断逻辑,比方是否是无效的 xml 开始字符:
private static boolean isValidStartCharForXmlElement(final byte b) {return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' || b == ':' || b == '_';}
是否是正文:
private static boolean isCommentBlockStart(final ByteBuf in, final int i) {return i < in.writerIndex() - 3
&& in.getByte(i + 2) == '-'
&& in.getByte(i + 3) == '-';
}
是否是 CDATA 数据:
private static boolean isCDATABlockStart(final ByteBuf in, final int i) {return i < in.writerIndex() - 8
&& in.getByte(i + 2) == '['
&& in.getByte(i + 3) == 'C'
&& in.getByte(i + 4) == 'D'
&& in.getByte(i + 5) == 'A'
&& in.getByte(i + 6) == 'T'
&& in.getByte(i + 7) == 'A'
&& in.getByte(i + 8) == '[';
通过应用这些办法判断好 xml 数据的起始地位之后,就能够调用 extractFrame 办法将要应用的 ByteBuf 从原始数据中拷贝进去,最初放到 out 中去:
final ByteBuf frame =
extractFrame(in, readerIndex + leadingWhiteSpaceCount, xmlElementLength - leadingWhiteSpaceCount);
in.skipBytes(xmlElementLength);
out.add(frame);
XmlDecoder
将 xml 数据拆分成为一个个 frame 之后,接下来就是对 xml 中具体数据的解析了。
netty 提供了一个 xml 数据解析的办法叫做 XmlDecoder, 次要用来对曾经是一个独自的 xml 数据的 frame 进行本质内容的解析,它的定义如下:
public class XmlDecoder extends ByteToMessageDecoder
XmlDecoder 依据读取到的 xml 内容,将 xml 的局部拆分为 XmlElementStart,XmlAttribute,XmlNamespace,XmlElementEnd,XmlProcessingInstruction,XmlCharacters,XmlComment,XmlSpace,XmlDocumentStart,XmlEntityReference,XmlDTD 和 XmlCdata。
这些数据基本上笼罩了 xml 中所有可能呈现的元素。
所有的这些元素都是定义在 io.netty.handler.codec.xml 包中的。
然而 XmlDecoder 对 xml 的读取解析则是借用了第三方 xml 工具包:fasterxml。
XmlDecoder 应用了 fasterxml 中的 AsyncXMLStreamReader 和 AsyncByteArrayFeeder 用来进行 xml 数据的解析。
这两个属性的定义如下:
private static final AsyncXMLInputFactory XML_INPUT_FACTORY = new InputFactoryImpl();
private final AsyncXMLStreamReader<AsyncByteArrayFeeder> streamReader;
private final AsyncByteArrayFeeder streamFeeder;
this.streamReader = XML_INPUT_FACTORY.createAsyncForByteArray();
this.streamFeeder = (AsyncByteArrayFeeder)this.streamReader.getInputFeeder();
decode 的逻辑是通过判断 xml element 的类型来别离进行不同数据的读取, 最初将读取到的数据封装成下面咱们提到的各种 xml 对象,最初将 xml 对象增加到 out list 中返回。
总结
咱们能够借助 XmlFrameDecoder 和 XmlDecoder 来实现十分不便的 xml 数据解析,netty 曾经为咱们造好轮子了,咱们就不须要再自行创造了。
本文已收录于 http://www.flydean.com/14-7-netty-codec-xml/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!