-
2021-09-24 00:21:28
什么时候需要序列化?
当你程序里的对象要存储到硬盘或者通过网络传输的时候,你的对象就需要序列化成二进制字节数组。那么,序列化对我们的程序到底有多大影响,如何判断一个序列化框架的好坏?我们一般从如下几个方面来评定一个序列化框架的优劣。
1、序列化反序列化的速度->直接影响了程序的性能。
2、序列化后字节流的大小->可能会占据你更多的带宽,特别是外网带宽很贵。
3、是否跨平台跨语言->影响了你程序的拓展性。
接下来介绍一下我常用的三种序列化方式。
一、Jdk序列化
Jdk序列化是最先接触的序列化方式,一个对象需要序列化,需要实现Serializable接口,并改写serialVersionUID字段值。
它会将类的信息和状态信息一起序列化到一个二进制数组里,保存到磁盘或者通过网络传输,当我们从磁盘或者网络收到这个二进制数组时,就可以通过这个数据进行反序列化来还原我们程序中的对象,但是这个序列化方式有如下缺点,一般不建议使用:
1、序列化反序列化的性能较长,会导致程序性能变慢。
2、序列化产生的码流较大,会占用更多的磁盘和带宽。
3、只能在Java里使用,不能跨语言使用。
二、Json序列化
在Json序列化之前,我们还用过xml的方式来保存数据,json相比xml,精简了不少,从xml的标签对改为了json了字段名,也不用进行繁重的dom解析,现在的xml我们常见的是一些配置文件采用了这个格式。
Json序列化是目前我工作中用到最主要的序列化方式,不管是Http的调用,还是缓存数据的序列化,90%以上都是用这个,有一批优秀的Json序列化框架,如FastJson、Jackson等。
三、ProtoBuf序列化
ProtoBuf序列化是公认的最好的序列化框架之一。具有极佳的序列化反序列化速度和极小的码流,非常适合追求极致性能的应用程序。
1、这么优秀的原因:
把数据变小一点,对于一条数据,json的表示方法:
{ "age": 30, "name": "zhangsan", "height": 175.33, "weight": 140 }
protobuf省去了很多中间冗余的{}"":,等,同时传输的过程中省略了null值
采用tag技术,替代json的key/value,使用tag的话,一般只占用1个字节,而json的key一个字符就占1个字节
在传输的过程中,会对整数进行压缩,例如一个long类型的1,会压缩成byte类型的1,这些是在内部操作的,对开发而言,是透明的
protobuf是采用tag/leg/value的方式来保存数据的,读到某个字段(tag)后,直接获取长度leg,解析出来就是所需要的值,而json需要解析字符串才可以办到。
2、如何使用:
引入依赖:
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.5.1</version> <scope>compile</scope> </dependency>
编写proto文件
syntax = "proto3"; option java_package = "chen.huai.jie.sdn.message.proto"; message StoreContent { string topic =1; string key =2; bytes message=3; }
导入protoc.exe可执行文件
利用命令来生成proto对应的java文件
protoc --java_out=..\java --proto_path=. *.proto
最终生了我们java里需要用到的文件,每次改动proto文件后都需要重新生成。
接下来我们就可以在代码里利用proto快乐的玩耍了。
关于protobuf的性能,可以参考我另一篇博客,里面有对json和protobuf的序列化性能对比
Java项目的缓存方案_ChenHuaiJie0912的博客-CSDN博客_java缓存方案
总结:
不是说没有其他的序列化方式,只是我对这三种用的最多,其他的用过了我也会在这里继续补充。极致性能体验推荐使用protobuf,真的不错。
更多相关内容 -
protobuf常用序列化和反序列化API
2017-06-29 17:27:16protobuf常用序列化和反序列化API,相关教程:http://blog.csdn.net/tennysonsky/article/details/73920767 -
java 常用序列化和反序列化框架使用demo
2015-07-16 16:59:41java 常用序列化和反序列化框架使用demo ,java 常用序列化和反序列化框架使用demo -
序列化的几种方式及区别
2021-01-17 17:02:43序列化的几种方式 什么是序列化? 内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流...常见的序列化方式有三种: 1.Java原生序列化 Java类通过实现Serializable接口来实现序列化的几种方式
什么是序列化?
内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流的过程称为对象的序列化(Serialization)。反之,将二进制流恢复为数据对象的过程称为反序列化(Deserialization)。序列化需要保留充分的信息以恢复数据对象,但是为了节约存储空间和网络带宽,序列化后的二进制流又要尽可能小。序列化常见的使用场景是RPC框架的数据传输。常见的序列化方式有三种:
1.Java原生序列化
Java类通过实现Serializable接口来实现该类对象的序列化,这个接口非常特殊,没有任何方法,只起标识作用.Java序列化保留了对象类的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。
实现Serializable接口的类建议设置serialVersionUID字段值,如果不设置,那么每次运行时,编译器会根据类的内部实现,包括类名、接口名、方法和属性等来自动生成serialVersionUID。如果类的源代码有修改,那么重新编译后serial VersionUID的取值可能会发生变化。因此实现Serializable接口的类一定要显式地定义serialVersionUID属性值。修改类时需要根据兼容性决定是否修改serialVersionUID值:
1.如果是兼容升级,请不要修改serialVersionUID字段,避免反序列化失败。2.如果是不兼容升级,需要修改serialVersionUID值,避免反序列化混乱。
使用Java原生序列化需注意,Java反序列化时不会调用类的无参构造方法,而是调用native方法将成员变量赋值为对应类型的初始值。基于性能及兼容性考虑,不推荐使用Java 原生序列化
demo例子
序列化方式一: 实现Serializable接口(隐式序列化)
通过实现Serializable接口,这种是隐式序列化(不需要手动),这种是最简单的序列化方式,会自动序列化所有非static和 transient关键字修饰的成员变量。
class Student implements Serializable{ private String name; private int age; public static int QQ = 1234; private transient String address = "CHINA"; Student(String name, int age ){ this.name = name; this.age = age; } public String toString() { return "name: " + name + "\n" +"age: " + age + "\n" +"QQ: " + QQ + "\n" + "address: " + address; } public void SetAge(int age) { this.age = age; } } public class SerializableDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { //创建可序列化对象 System.out.println("原来的对象:"); Student stu = new Student("Ming", 16); System.out.println(stu); //创建序列化输出流 ByteArrayOutputStream buff = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(buff); //将序列化对象存入缓冲区 out.writeObject(stu); //修改相关值 Student.QQ = 6666; // 发现打印结果QQ的值被改变 stu.SetAge(18); //发现值没有被改变 //从缓冲区取回被序列化的对象 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray())); Student newStu = (Student) in.readObject(); System.out.println("序列化后取出的对象:"); System.out.println(newStu); } }
打印结果:
原来的对象:
带有参数的构造方法
name: Ming
age: 16
QQ: 1234
address: CHINA
序列化后取出的对象:
name: Ming
age: 16
QQ: 6666
address: null发现address(被transient)和QQ(被static)也没有被序列化,中途修改QQ的值是为了以防读者误会QQ被序列化了。因为序列化可以保存对象的状态,但是QQ的值被改变了,说明没有被序列化。static成员不属于对象实例,可能被别的对象修改没办法序列化,序列化是序列对象。对于address被反序列化后由于没有对应的引用,所以为null。而且Serializable不会调用构造方法。
PS:细心的可能发现序列化很诱人,可以保存对象的初始信息,在以后可以回到这个初始状态。序列化方式二:实现Externalizable接口。(显式序列化)
Externalizable接口继承自Serializable, 我们在实现该接口时,必须实现writeExternal()和readExternal()方法,而且只能通过手动进行序列化,并且两个方法是自动调用的,因此,这个序列化过程是可控的,可以自己选择哪些部分序列化
public class Blip implements Externalizable{ private int i ; private String s; public Blip() {} public Blip(String x, int a) { System.out.println("Blip (String x, int a)"); s = x; i = a; } public String toString() { return s+i; } @Override public void writeExternal(ObjectOutput out) throws IOException { // TODO Auto-generated method stub System.out.println("Blip.writeExternal"); out.writeObject(s); out.writeInt(i); // } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub System.out.println("Blip.readExternal"); s = (String)in.readObject(); i = in.readInt(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { System.out.println("Constructing objects"); Blip b = new Blip("A Stirng", 47); System.out.println(b); ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("F://Demo//file1.txt")); System.out.println("保存对象"); o.writeObject(b); o.close(); //获得对象 System.out.println("获取对象"); ObjectInputStream in = new ObjectInputStream(new FileInputStream("F://Demo//file1.txt")); System.out.println("Recovering b"); b = (Blip)in.readObject(); System.out.println(b); } }
打印结果为:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal A Stirng47
当注释掉writeExternal和readExternal方法后打印结果为:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal null0
说明:Externalizable类会调用public的构造函数先初始化对象,在调用所保存的内容将对象还原。假如构造方法不是public则会出现运行时错误。
序列化方式三:实现Serializable接口+添加writeObject()和readObject()方法。(显+隐序列化)
如果想将方式一和方式二的优点都用到的话,可以采用方式三, 先实现Serializable接口,并且添加writeObject()和readObject()方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须有相应的格式。
- 方法必须要被private修饰 —–>才能被调用
- 第一行调用默认的defaultRead/WriteObject() —–>隐式序列化非static和transient
- 调用read/writeObject()将获得的值赋给相应的值 —–>显式序列化
public class SerDemo implements Serializable{ public transient int age = 23; public String name ; public SerDemo(){ System.out.println("默认构造器。。。"); } public SerDemo(String name) { this.name = name; } private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(age); } private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { stream.defaultReadObject(); age = stream.readInt(); } public String toString() { return "年龄" + age + " " + name; } public static void main(String[] args) throws IOException, ClassNotFoundException { SerDemo stu = new SerDemo("Ming"); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(stu); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); SerDemo stu1 = (SerDemo) in.readObject(); System.out.println(stu1); } }
打印结果为
年龄23 Ming
注释掉stream.writeInt(age)和age= stream.readInt()后:
年龄0 Ming
方式三结合了显式和隐式序列化,Ming被正常序列化,由于age被trancient修饰,所以需要显式序列化。
2.Hessian 序列化
Hessian 序列化是一种支持动态类型、跨语言、基于对象
传输的网络协议。 Java 对象序列化的二进制流可以被其他语言 ( 如 C++、 Python )反
序列化。 Hessian 协议具有如下特性.
自描述序列化类型。不依赖外部描述文件或接口定义 , 用一个字节表示常用
基础类型 , 极大缩短二进制流。
· 语言无关,支持脚本语言。
· 协议简单,比 Java 原生序列化高效。
相比 Hessian 1.0, Hessian 2.0 中增加了压缩编码,其序列化二进制流大小是 Java
序列化的 50% , 序列化耗时是 Java 序列化的 30% ,反序列化耗时是 Java 反序列化的
20% 。
Hessian 会把复杂对象所有属性存储在一个 Map 申 进行序列化。所以在父类、子
类存在同名成员变量的情况下, Hessian 序列化时,先序列化子类 ,然后序列化父类,
因此反序列化结果会导致子类同名成员变量被父类的值覆盖。3.Json序列化
JSON ( JavaScript O同ect Notation )是一种轻量级的数据交
换格式。 JSON 序列化就是将数据对象转换为 JSON 字符串。在序列化过程中抛弃了
类型信息,所以反序列化时只有提供类型信息才能准确地反序列化。相比前两种方式,
JSON 可读性比较好,方便调试。
序列化通常会通过网络传输对象 , 而对象中往往有敏感数据,所以序列化常常
成为黑客的攻击点,攻击者巧妙地利用反序列化过程构造恶意代码,使得程序在反序
列化的过程中执行任意代码。 Java 工程中广泛使用的 Apache Commons Collections 、
Jackson 、 fastjson 等都出现过反序列化漏洞。如何防范这种黑客攻击呢?有些对象的
敏感属性不需要进行序列化传输 ,可以加 transient 关键字,避免把此属性信息转化为
序列化的二进制流。如果一定要传递对象的敏感属性,可以使用对称与非对称加密方
式独立传输,再使用某个方法把属性还原到对象中。应用开发者对序列化要有一定的
安全防范意识 , 对传入数据的内容进行校验或权限控制,及时更新安全漏洞,避免受
到攻击。https://blog.csdn.net/javaer_lee/article/details/89098754
https://www.jianshu.com/p/7298f0c559dc
-
几种常用序列化和反序列化方法
2019-12-30 21:48:38序列化和反序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持久化。...摘要
序列化和反序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持久化。然而,序列化和反序列化的选型却是系统设计或重构一个重要的环节,在分布式、大数据量系统设计里面更为显著。恰当的序列化协议不仅可以提高系统的通用性、强健性、安全性、优化系统性能,而且会让系统更加易于调试、便于扩展。本文从多个角度去分析和讲解“序列化和反序列化”,并对比了当前流行的几种序列化协议,期望对读者做序列化选型有所帮助。
简介
文章作者服务于美团推荐与个性化组,该组致力于为美团用户提供每天billion级别的高质量个性化推荐以及排序服务。从Terabyte级别的用户行为数据,到Gigabyte级别的Deal/Poi数据;从对实时性要求毫秒以内的用户实时地理位置数据,到定期后台job数据,推荐与重排序系统需要多种类型的数据服务。推荐与重排序系统客户包括各种内部服务、美团客户端、美团网站。为了提供高质量的数据服务,为了实现与上下游各系统进行良好的对接,序列化和反序列化的选型往往是我们做系统设计的一个重要考虑因素。
本文内容按如下方式组织:
-
第一部分给出了序列化和反序列化的定义,以及其在通讯协议中所处的位置。
-
第二部分从使用者的角度探讨了序列化协议的一些特性。
-
第三部分描述在具体的实施过程中典型的序列化组件,并与数据库组建进行了类比。
-
第四部分分别讲解了目前常见的几种序列化协议的特性,应用场景,并对相关组件进行举例。
-
最后一部分,基于各种协议的特性,以及相关benchmark数据,给出了作者的技术选型建议。
一、定义以及相关概念
互联网的产生带来了机器间通讯的需求,而互联通讯的双方需要采用约定的协议,序列化和反序列化属于通讯协议的一部分。通讯协议往往采用分层模型,不同模型每层的功能定义以及颗粒度不同,例如:TCP/IP协议是一个四层协议,而OSI模型却是七层协议模型。在OSI七层协议模型中展现层(Presentation Layer)的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象–这两个功能就是序列化和反序列化。一般而言,TCP/IP协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,所以序列化协议属于TCP/IP协议应用层的一部分。本文对序列化协议的讲解主要基于OSI七层协议模型。
-
序列化: 将数据结构或对象转换成二进制串的过程
-
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
数据结构、对象与二进制串
不同的计算机语言中,数据结构,对象以及二进制串的表示方式并不相同。
数据结构和对象:对于类似Java这种完全面向对象的语言,工程师所操作的一切都是对象(Object),来自于类的实例化。在Java语言中最接近数据结构的概念,就是POJO(Plain Old Java Object)或者Javabean--那些只有setter/getter方法的类。而在C++这种半面向对象的语言中,数据结构和struct对应,对象和class对应。
二进制串:序列化所生成的二进制串指的是存储在内存中的一块数据。C++语言具有内存操作符,所以二进制串的概念容易理解,例如,C++语言的字符串可以直接被传输层使用,因为其本质上就是以'\0'结尾的存储在内存中的二进制串。在Java语言里面,二进制串的概念容易和String混淆。实际上String 是Java的一等公民,是一种特殊对象(Object)。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。二进制串在Java里面所指的是byte[],byte是Java的8中原生数据类型之一(Primitive data types)。
二、序列化协议特性
每种序列化协议都有优点和缺点,它们在设计之初有自己独特的应用场景。在系统设计的过程中,需要考虑序列化需求的方方面面,综合对比各种序列化协议的特性,最终给出一个折衷的方案。
通用性
通用性有两个层面的意义:
第一、技术层面,序列化协议是否支持跨平台、跨语言。如果不支持,在技术层面上的通用性就大大降低了。
第二、流行程度,序列化和反序列化需要多方参与,很少人使用的协议往往意味着昂贵的学习成本;另一方面,流行度低的协议,往往缺乏稳定而成熟的跨语言、跨平台的公共包。强健性/鲁棒性
以下两个方面的原因会导致协议不够强健:
第一、成熟度不够,一个协议从制定到实施,到最后成熟往往是一个漫长的阶段。协议的强健性依赖于大量而全面的测试,对于致力于提供高质量服务的系统,采用处于测试阶段的序列化协议会带来很高的风险。
第二、语言/平台的不公平性。为了支持跨语言、跨平台的功能,序列化协议的制定者需要做大量的工作;但是,当所支持的语言或者平台之间存在难以调和的特性的时候,协议制定者需要做一个艰难的决定–支持更多人使用的语言/平台,亦或支持更多的语言/平台而放弃某个特性。当协议的制定者决定为某种语言或平台提供更多支持的时候,对于使用者而言,协议的强健性就被牺牲了。可调试性/可读性
序列化和反序列化的数据正确性和业务正确性的调试往往需要很长的时间,良好的调试机制会大大提高开发效率。序列化后的二进制串往往不具备人眼可读性,为了验证序列化结果的正确性,写入方不得同时撰写反序列化程序,或提供一个查询平台–这比较费时;另一方面,如果读取方未能成功实现反序列化,这将给问题查找带来了很大的挑战–难以定位是由于自身的反序列化程序的bug所导致还是由于写入方序列化后的错误数据所导致。对于跨公司间的调试,由于以下原因,问题会显得更严重:
第一、支持不到位,跨公司调试在问题出现后可能得不到及时的支持,这大大延长了调试周期。
第二、访问限制,调试阶段的查询平台未必对外公开,这增加了读取方的验证难度。如果序列化后的数据人眼可读,这将大大提高调试效率, XML和JSON就具有人眼可读的优点。
性能
性能包括两个方面,时间复杂度和空间复杂度:
第一、空间开销(Verbosity), 序列化需要在原有的数据上加上描述字段,以为反序列化解析之用。如果序列化过程引入的额外开销过高,可能会导致过大的网络,磁盘等各方面的压力。对于海量分布式存储系统,数据量往往以TB为单位,巨大的的额外空间开销意味着高昂的成本。
第二、时间开销(Complexity),复杂的序列化协议会导致较长的解析时间,这可能会使得序列化和反序列化阶段成为整个系统的瓶颈。可扩展性/兼容性
移动互联时代,业务系统需求的更新周期变得更快,新的需求不断涌现,而老的系统还是需要继续维护。如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大提供系统的灵活度。
安全性/访问限制
在序列化选型的过程中,安全性的考虑往往发生在跨局域网访问的场景。当通讯发生在公司之间或者跨机房的时候,出于安全的考虑,对于跨局域网的访问往往被限制为基于HTTP/HTTPS的80和443端口。如果使用的序列化协议没有兼容而成熟的HTTP传输层框架支持,可能会导致以下三种结果之一:
第一、因为访问限制而降低服务可用性。
第二、被迫重新实现安全协议而导致实施成本大大提高。
第三、开放更多的防火墙端口和协议访问,而牺牲安全性。三、序列化和反序列化的组件
典型的序列化和反序列化过程往往需要如下组件:
-
IDL(Interface description language)文件:参与通讯的各方需要对通讯的内容需要做相关的约定(Specifications)。为了建立一个与语言和平台无关的约定,这个约定需要采用与具体开发语言、平台无关的语言来进行描述。这种语言被称为接口描述语言(IDL),采用IDL撰写的协议约定称之为IDL文件。
-
IDL Compiler:IDL文件中约定的内容为了在各语言和平台可见,需要有一个编译器,将IDL文件转换成各语言对应的动态库。
-
Stub/Skeleton Lib:负责序列化和反序列化的工作代码。Stub是一段部署在分布式系统客户端的代码,一方面接收应用层的参数,并对其序列化后通过底层协议栈发送到服务端,另一方面接收服务端序列化后的结果数据,反序列化后交给客户端应用层;Skeleton部署在服务端,其功能与Stub相反,从传输层接收序列化参数,反序列化后交给服务端应用层,并将应用层的执行结果序列化后最终传送给客户端Stub。
-
Client/Server:指的是应用层程序代码,他们面对的是IDL所生存的特定语言的class或struct。
-
底层协议栈和互联网:序列化之后的数据通过底层的传输层、网络层、链路层以及物理层协议转换成数字信号在互联网中传递。
序列化组件与数据库访问组件的对比
数据库访问对于很多工程师来说相对熟悉,所用到的组件也相对容易理解。下表类比了序列化过程中用到的部分组件和数据库访问组件的对应关系,以便于大家更好的把握序列化相关组件的概念。
序列化组件
数据库组件
说明
IDL
DDL
用于建表或者模型的语言
DL file
DB Schema
表创建文件或模型文件
Stub/Skeleton lib
O/R mapping
将class和Table或者数据模型进行映射
四、几种常见的序列化和反序列化协议
互联网早期的序列化协议主要有COM和CORBA。
COM主要用于Windows平台,并没有真正实现跨平台,另外COM的序列化的原理利用了编译器中虚表,使得其学习成本巨大(想一下这个场景, 工程师需要是简单的序列化协议,但却要先掌握语言编译器)。由于序列化的数据与编译器紧耦合,扩展属性非常麻烦。
CORBA是早期比较好的实现了跨平台,跨语言的序列化协议。COBRA的主要问题是参与方过多带来的版本过多,版本之间兼容性较差,以及使用复杂晦涩。这些政治经济,技术实现以及早期设计不成熟的问题,最终导致COBRA的渐渐消亡。J2SE 1.3之后的版本提供了基于CORBA协议的RMI-IIOP技术,这使得Java开发者可以采用纯粹的Java语言进行CORBA的开发。
这里主要介绍和对比几种当下比较流行的序列化协议,包括XML、JSON、Protobuf、Thrift和Avro。
一个例子
如前所述,序列化和反序列化的出现往往晦涩而隐蔽,与其他概念之间往往相互包容。为了更好了让大家理解序列化和反序列化的相关概念在每种协议里面的具体实现,我们将一个例子穿插在各种序列化协议讲解中。在该例子中,我们希望将一个用户信息在多个系统里面进行传递;在应用层,如果采用Java语言,所面对的类对象如下所示:
class Address{ private String city; private String postcode; private String street;} publicclass UserInfo { private Integer userid; private String name; private List<Address> address;}
XML&SOAP
XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。 XML历史悠久,其1.0版本早在1998年就形成标准,并被广泛使用至今。XML的最初产生目标是对互联网文档(Document)进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂(Verbose and Complex)。 XML本质上是一种描述语言,并且具有自我描述(Self-describing)的属性,所以XML自身就被用于XML序列化的IDL。 标准的XML描述格式有两种:DTD(Document Type Definition)和XSD(XML Schema Definition)。作为一种人眼可读(Human-readable)的描述语言,XML被广泛使用在配置文件中,例如O/R mapping、 Spring Bean Configuration File 等。
SOAP(Simple Object Access protocol) 是一种被广泛应用的,基于XML为序列化和反序列化协议的结构化消息传递协议。SOAP在互联网影响如此大,以至于我们给基于SOAP的解决方案一个特定的名称–Web service。SOAP虽然可以支持多种传输层协议,不过SOAP最常见的使用方式还是XML+HTTP。SOAP协议的主要接口描述语言(IDL)是WSDL(Web Service Description Language)。SOAP具有安全、可扩展、跨语言、跨平台并支持多种传输层协议。如果不考虑跨平台和跨语言的需求,XML的在某些语言里面具有非常简单易用的序列化使用方法,无需IDL文件和第三方编译器, 例如Java+XStream。
自我描述与递归
SOAP是一种采用XML进行序列化和反序列化的协议,它的IDL是WSDL. 而WSDL的描述文件是XSD,而XSD自身是一种XML文件。 这里产生了一种有趣的在数学上称之为“递归”的问题,这种现象往往发生在一些具有自我属性(Self-description)的事物上。
IDL文件举例
采用WSDL描述上述用户基本信息的例子如下:
< xsd:complexType name = 'Address' > < xsd:attribute name = 'city' type = 'xsd:string' /> < xsd:attribute name = 'postcode' type = 'xsd:string' /> < xsd:attribute name = 'street' type = 'xsd:string'/> </ xsd:complexType > < xsd:complexType name = 'UserInfo' > < xsd:sequence > < xsd:element name = 'address' type = 'tns:Address' /> < xsd:element name = 'address1' type = 'tns:Address'/> </ xsd:sequence > < xsd:attribute name = 'userid' type = 'xsd:int' /> < xsd:attribute name = 'name' type = 'xsd:string' /> </ xsd:complexType >
典型应用场景和非应用场景
SOAP协议具有广泛的群众基础,基于HTTP的传输协议使得其在穿越防火墙时具有良好安全特性,XML所具有的人眼可读(Human-readable)特性使得其具有出众的可调试性,互联网带宽的日益剧增也大大弥补了其空间开销大(Verbose)的缺点。对于在公司之间传输数据量相对小或者实时性要求相对低(例如秒级别)的服务是一个好的选择。
由于XML的额外空间开销大,序列化之后的数据量剧增,对于数据量巨大序列持久化应用常景,这意味着巨大的内存和磁盘开销,不太适合XML。另外,XML的序列化和反序列化的空间和时间开销都比较大,对于对性能要求在ms级别的服务,不推荐使用。WSDL虽然具备了描述对象的能力,SOAP的S代表的也是simple,但是SOAP的使用绝对不简单。对于习惯于面向对象编程的用户,WSDL文件不直观。
JSON(Javascript Object Notation)
JSON起源于弱类型语言Javascript, 它的产生来自于一种称之为"Associative array"的概念,其本质是就是采用"Attribute-value"的方式来描述对象。实际上在Javascript和PHP等弱类型语言中,类的描述方式就是Associative array。JSON的如下优点,使得它快速成为最广泛使用的序列化协议之一:
1、这种Associative array格式非常符合工程师对对象的理解。
2、它保持了XML的人眼可读(Human-readable)的优点。
http://www.codeproject.com/Articles/604720/JSON-vs-XML-Some-hard-numbers-about-verbosity
4、它具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。
5、与XML相比,其协议比较简单,解析速度比较快。
6、松散的Associative array使得其具有良好的可扩展性和兼容性。
IDL悖论
JSON实在是太简单了,或者说太像各种语言里面的类了,所以采用JSON进行序列化不需要IDL。这实在是太神奇了,存在一种天然的序列化协议,自身就实现了跨语言和跨平台。然而事实没有那么神奇,之所以产生这种假象,来自于两个原因:
第一、Associative array在弱类型语言里面就是类的概念,在PHP和Javascript里面Associative array就是其class的实际实现方式,所以在这些弱类型语言里面,JSON得到了非常良好的支持。
第二、IDL的目的是撰写IDL文件,而IDL文件被IDL Compiler编译后能够产生一些代码(Stub/Skeleton),而这些代码是真正负责相应的序列化和反序列化工作的组件。 但是由于Associative array和一般语言里面的class太像了,他们之间形成了一一对应关系,这就使得我们可以采用一套标准的代码进行相应的转化。对于自身支持Associative array的弱类型语言,语言自身就具备操作JSON序列化后的数据的能力;对于Java这强类型语言,可以采用反射的方式统一解决,例如Google提供的Gson。典型应用场景和非应用场景
JSON在很多应用场景中可以替代XML,更简洁并且解析速度更快。典型应用场景包括:
1、公司之间传输数据量相对小,实时性要求相对低(例如秒级别)的服务。
2、基于Web browser的Ajax请求。
3、由于JSON具有非常强的前后兼容性,对于接口经常发生变化,并对可调式性要求高的场景,例如Mobile app与服务端的通讯。
4、由于JSON的典型应用场景是JSON+HTTP,适合跨防火墙访问。总的来说,采用JSON进行序列化的额外空间开销比较大,对于大数据量服务或持久化,这意味着巨大的内存和磁盘开销,这种场景不适合。没有统一可用的IDL降低了对参与方的约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便,延长开发周期。 由于JSON在一些语言中的序列化和反序列化需要采用反射机制,所以在性能要求为ms级别,不建议使用。
IDL文件举例
以下是UserInfo序列化之后的一个例子:
{" userid ": 1 ," name ": "messi" ," address ": [{" city ": "北京" ," postcode ": "1000000" ," street ": "wangjingdonglu" }] }
Thrift
Thrift是Facebook开源提供的一个高性能,轻量级RPC服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。 但是,Thrift并不仅仅是序列化协议,而是一个RPC框架。相对于JSON和XML而言,Thrift在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC解决方案;但是由于Thrift的序列化被嵌入到Thrift框架里面,Thrift框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用(例如HTTP)。
典型应用场景和非应用场景
对于需求为高性能,分布式的RPC服务,Thrift是一个优秀的解决方案。它支持众多语言和丰富的数据类型,并对于数据字段的增删具有较强的兼容性。所以非常适用于作为公司内部的面向服务构建(SOA)的标准RPC框架。
不过Thrift的文档相对比较缺乏,目前使用的群众基础相对较少。另外由于其Server是基于自身的Socket服务,所以在跨防火墙访问时,安全是一个顾虑,所以在公司间进行通讯时需要谨慎。 另外Thrift序列化之后的数据是Binary数组,不具有可读性,调试代码时相对困难。最后,由于Thrift的序列化和框架紧耦合,无法支持向持久层直接读写数据,所以不适合做数据持久化序列化协议。
IDL文件举例
struct Address{ 1: required string city; 2: optional string postcode; 3: optional string street;} struct UserInfo{ 1: required string userid; 2: required i32 name; 3: optional list<Address> address;}
Protobuf
Protobuf具备了优秀的序列化协议的所需的众多典型特征:
1、标准的IDL和IDL编译器,这使得其对工程师非常友好。
2、序列化数据非常简洁,紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。
3、解析速度非常快,比对应的XML快约20-100倍。
4、提供了非常友好的动态库,使用非常简介,反序列化只需要一行代码。Protobuf是一个纯粹的展示层协议,可以和各种传输层协议一起使用;Protobuf的文档也非常完善。 但是由于Protobuf产生于Google,所以目前其仅仅支持Java、C++、Python三种语言。另外Protobuf支持的数据类型相对较少,不支持常量类型。由于其设计的理念是纯粹的展现层协议(Presentation Layer),目前并没有一个专门支持Protobuf的RPC框架。
典型应用场景和非应用场景
Protobuf具有广泛的用户基础,空间开销小以及高解析性能是其亮点,非常适合于公司内部的对性能要求高的RPC调用。由于Protobuf提供了标准的IDL以及对应的编译器,其IDL文件是参与各方的非常强的业务约束,另外,Protobuf与传输层无关,采用HTTP具有良好的跨防火墙的访问属性,所以Protobuf也适用于公司间对性能要求比较高的场景。由于其解析性能高,序列化后数据量相对少,非常适合应用层对象的持久化场景。
它的主要问题在于其所支持的语言相对较少,另外由于没有绑定的标准底层传输层协议,在公司间进行传输层协议的调试工作相对麻烦。
IDL文件举例
message Address{ required string city=1; optional string postcode=2; optional string street=3;}message UserInfo{ required string userid=1; required string name=2; repeated Address address=3;}
Avro
Avro的产生解决了JSON的冗长和没有IDL的问题,Avro属于Apache Hadoop的一个子项目。 Avro提供两种序列化格式:JSON格式或者Binary格式。Binary格式在空间开销和解析性能方面可以和Protobuf媲美,JSON格式方便测试阶段的调试。 Avro支持的数据类型非常丰富,包括C++语言里面的union类型。Avro支持JSON格式的IDL和类似于Thrift和Protobuf的IDL(实验阶段),这两者之间可以互转。Schema可以在传输数据的同时发送,加上JSON的自我描述属性,这使得Avro非常适合动态类型语言。 Avro在做文件持久化的时候,一般会和Schema一起存储,所以Avro序列化文件自身具有自我描述属性,所以非常适合于做Hive、Pig和MapReduce的持久化数据格式。对于不同版本的Schema,在进行RPC调用的时候,服务端和客户端可以在握手阶段对Schema进行互相确认,大大提高了最终的数据解析速度。
典型应用场景和非应用场景
Avro解析性能高并且序列化之后的数据非常简洁,比较适合于高性能的序列化服务。
由于Avro目前非JSON格式的IDL处于实验阶段,而JSON格式的IDL对于习惯于静态类型语言的工程师来说不直观。
IDL文件举例
protocol Userservice { record Address { string city ; string postcode ; string street ; } record UserInfo { string name ; int userid ; array<Address> address = [] ; }}
所对应的JSON Schema格式如下:
{ " protocol " : "Userservice" , " namespace " : "org.apache.avro.ipc.specific" , " version " : "1.0.5" , " types " : [ { " type " : "record" , " name " : "Address" , " fields " : [ { " name " : "city" , " type " : "string" }, { " name " : "postcode" , " type " : "string" }, { " name " : "street", " type " : "string" } ] }, { " type " : "record" , " name " : "UserInfo" , " fields " : [ { " name " : "name" , " type " : "string" }, { " name " : "userid" , " type " : "int" }, { " name " : "address" , " type " : { " type " : "array" , " items " : "Address" } , " default " : [ ] } ] } ] , " messages " : { } }
五、Benchmark以及选型建议
Benchmark
以下数据来自 https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
解析性能
序列化之空间开销
从上图可得出如下结论:
1、XML序列化(Xstream)无论在性能和简洁性上比较差。
2、Thrift与Protobuf相比在时空开销方面都有一定的劣势。
3、Protobuf和Avro在两方面表现都非常优越。选型建议
以上描述的五种序列化和反序列化协议都各自具有相应的特点,适用于不同的场景:
1、对于公司间的系统调用,如果性能要求在100ms以上的服务,基于XML的SOAP协议是一个值得考虑的方案。
2、基于Web browser的Ajax,以及Mobile app与服务端之间的通讯,JSON协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON也是非常不错的选择。
3、对于调试环境比较恶劣的场景,采用JSON或XML能够极大的提高调试效率,降低系统开发成本。
4、当对性能和简洁性有极高要求的场景,Protobuf,Thrift,Avro之间具有一定的竞争关系。
5、对于T级别的数据的持久化应用场景,Protobuf和Avro是首要选择。如果持久化后的数据存储在Hadoop子项目里,Avro会是更好的选择。
6、由于Avro的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro是更好的选择。
7、对于持久层非Hadoop项目,以静态类型语言为主的应用场景,Protobuf会更符合静态类型语言工程师的开发习惯。
8、如果需要提供一个完整的RPC解决方案,Thrift是一个好的选择。
9、如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf可以优先考虑。 -
-
用到的几种序列化方式小结
2018-12-24 20:56:32什么是序列化 团队里系统间的数据传输与接收,主要是...java原生序列化方式,实现了java.io.Serializable接口的类,可以被执行串行化操作。通过serialVersionUID来验证版本的一致性,否则抛出异常。它序列化出...什么是序列化
团队里系统间的数据传输与接收,主要是metaq、hsf、tair等中间件系统,经常要用到序列化与反序列化,使用的方法也是多种多样,那么,到底该如何抉择呢?
如何序列化
java
介绍
java原生序列化方式,实现了
java.io.Serializable
接口的类,可以被执行串行化操作。通过serialVersionUID
来验证版本的一致性,否则抛出异常。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。问题点
- 如果内部的字段的类没有实现java.io.Serializable,也会报错。
- 子类实现了java.io.Serializable接口,父类没有,则子类中属性能正确序列化,父类中的属性无法序列化,数据丢失。
使用
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; public class CommonUtils { public static Serializable byteArray2Object(byte[] body) { ByteArrayInputStream bis = null; ObjectInputStream oi = null; try { bis = new ByteArrayInputStream(body); oi = new ObjectInputStream(bis); return (Serializable) oi.readObject(); } catch (IOException e) { throw new RuntimeException("deserialize object failed.", e); } catch (ClassNotFoundException e) { throw new RuntimeException("deserialize object class not found.", e); } finally { closeInputStream(oi); closeInputStream(bis); } } public static byte[] object2ByteArray(Serializable obj) { ByteArrayOutputStream bos = null; ObjectOutputStream out = null; try { bos = new ByteArrayOutputStream(); out = new ObjectOutputStream(bos); out.writeObject(obj); out.flush(); return bos.toByteArray(); } catch (IOException e) { throw new RuntimeException("serialize object failed."); } finally { closeOutputStream(out); closeOutputStream(bos); } } public static void closeOutputStream(OutputStream out) { if (out != null) { try { out.close(); } catch (Exception e) { out = null; } } } public static void closeInputStream(InputStream bis) { if (bis != null) { try { bis.close(); } catch (Exception e) { bis = null; } } } }
note:此时序列化的对象必须要实现Serializable接口,否则会报序列化异常。
public class XXXDTO implements Serializable { private static final long serialVersionUID = -4430726442401404564L; ... }
hessian
介绍
hessian原本是一个RPC框架,基于http传输,通过自定义的序列化协议将数据转换成二进制流,支持跨语言兼容性。由于其中序列化协议在稳定性和效率的折中上表现优异,切接入零成本,被广泛在互联网应用中使用。SerializerFactory继承AbstractSerializerFactory,而且在SerializerFactory有很多静态map用来存放类与序列化和反序列化工具类的映射,这样如果已经用过的序列化工具就可以直接拿出来用,不必再重新实例化工具类。问题点
用hessian序列化对象是一个对象继承另外一个对象的时候,当一个属性在子类和有一个相同属性的时候,反序列化后子类属性总是为null。
如果一个对象继承于HashMap,并且增加属性,这些增加的属性不会被序列化。
使用
ByteArrayOutputStream out = outputStream(data); Hessian2StreamingOutput hout = new Hessian2StreamingOutput(out); hout.writeObject(data); return out.toByteArray();
Protostuff
proto stuff是一个集成了多重协议的序列化框架。像它的名字一样,它也提供了对protobuf协议的支持。支持运行时生成schema的runtime方式,不用手写,开发接入成本低。
用法
protected static final int BUFFER_SIZE = 1024; private static ThreadLocal<LinkedBuffer> bufferCached = new ThreadLocal<LinkedBuffer>(){ @Override protected LinkedBuffer initialValue(){ return LinkedBuffer.allocate(BUFFER_SIZE); } }; @SuppressWarnings({"unchecked" }) public static byte[] serialize(Object object){ try{ return ProtostuffIOUtil.toByteArray(object, getSchema(object.getClass()), bufferCached.get()); }finally{ bufferCached.get().clear(); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private static Schema getSchema(Class clazz) { return RuntimeSchema.getSchema(clazz); } @SuppressWarnings("unchecked") public static void deserialize(Object object, byte[] bytes){ ProtostuffIOUtil.mergeFrom(bytes, object, getSchema(object.getClass())); }
JSON序列化
JSON的格式本身有强语义,可读。把它作为中间结果,便于问题排查。其中较快的库有alibaba的fastJson。把它用作序列化框架,其特点有:
-
中间结果可读,易于排查
-
跨平台、跨语言支持
-
速度快、但体积大
-
支持引用类型,支持循环引用。
-
由于多了层JSON的解析,引入了更多稳定应隐患,对继承、泛型的支持也不够好。
-
小結
方式 优点 缺点 目前使用场景 java原生序列化 稳定无bug,对Java继承体系提供完整的支持
性能差
非java语言无法使用
notify、hsf可选 hessian 性能好
跨语言
兼容性:支持引用,继承、泛型支持差 hsf默认 proto stuff 有多种协议供选择
兼容protobuf
跨语言
兼容性:继承、泛型支持差 metaq json 开发成本低 文件大 http接口返回封装 简单小结下,待后续完善...
-
redis序列化方式
2019-04-25 14:38:00redis序列化方式 (内容复制别人的博客,留作学习笔记) spring-data-redis的序列化类有下面这几个: GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化 Jackson2JsonRedisSerializer: 跟... -
序列化几种方式
2018-06-27 22:52:010、前言本文主要对几种常见Java序列化方式进行实现。包括Java原生以流的方法进行的序列化、Json序列化、FastJson序列化、Protobuff序列化。1、Java原生序列化Java原生序列化方法即通过Java原生流(InputStream和... -
常用的c++序列化方法
2019-09-03 18:46:531、 什么是序列化? 程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将 程序数据转化成能被存储并传输的格式的过程被... -
Java IO篇:序列化与反序列化
2022-01-12 03:22:50这个编码和解码的过程称之为序列化和反序列化。所以序列化就是把 Java 对象变成二进制形式,本质上就是一个byte[]数组。将对象序列化之后,就可以写入磁盘进行保存或者通过网络中输出给远程服务了。反之,反序列化... -
protobuf入门教程(三):常用序列化/反序列化接口
2017-06-29 17:24:13C数组的序列化和反序列化#include "addressbook.pb.h" #include using namespace std;/* //C数组的序列化和序列化API //在/usr/local/include/google/目录下,查找包含"SerializeToArray"所有的文件,同时打印所在... -
RedisTemplate 常用方法、序列化方式、基于 Redis 实现分布式锁
2019-11-27 19:49:16RedisTemplate 序列化方式 RedisTemplate 常用方法 org.springframework.data.redis.core.RedisTemplate 常用方法(本文环境 Spring Boot 2.1.3): 方法 描述 Boolean expire(K key, final long timeout, f..... -
Dubbo的多种序列化算法
2020-10-14 03:51:56既然需要网络通信,那就必然会使用到序列化与反序列化的相关技术,Dubbo 也不例外。 1 JDK序列化操作 1.1 实现 Serializable 接口 被序列化对象实现 Serializable 接口。 public class Student implements ... -
Spring Feign 序列化机制
2020-12-18 19:03:30Spring Cloud封装Feign,直接使用Spring MVC注解以及HttpMessageConverters来序列化。Spring Boot有autoconfigure机制,当spring boot中引入了spring-web jar包的时候,HttpMessageConvertersAutoConfiguration便会... -
各种常用的序列化性能的对比
2018-01-14 06:45:00各种常用的序列化性能的对比 https://github.com/eishay/jvm-serializers/wiki 里面还有代码,可以查看序列化比较的过程; -
springboot集成redis序列化问题汇总
2022-04-03 22:54:09两种方案中都需要对序列化方式进行设置,否则会出现二进制格式的数据(不论是Redis desktop manager工具还是redis自带客户端黑窗口打开),如果需要查看数据则会影响观感,关于序列化方式设置底层都是对RedisSerializer... -
【Java杂记】序列化:从Java序列化到多种序列化方式
2020-10-10 19:36:36需要一种方式:Java对象 —序列化–> 保存/传输 –反序列化–> Java对象 序列化(编码): 把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程 Java对象 –编码协议–> ... -
Redis序列化、RedisTemplate序列化方式大解读,介绍Genericjackson2jsonredisserializer序列化器的坑
2019-06-26 16:56:45前言 上一篇已经介绍了优雅的操作...下几种常用的序列化方式 最近在做一个项目,由于并发量大,大量使用到了RedisTemplate来操作Redis。但使用过程中,遇到了不少的坑,各种翻看源码 来跟踪,也总结出了不少的经... -
Java序列化,如何实现序列化和反序列化,常见的序列化协议有哪些?
2018-05-09 22:22:58Java序列化定义 将那些实现了Serializable接口的...Java序列化的作用 Java远程方法调用(RMI) 对JavaBeans进行序列化如何实现序列化和反序列化 实现序列化的方法 1. 实现Serializable接口 --该接口是一个可序... -
深入分析序列化和反序列化原理,终于知道serialVersionUID到底有什么用了
2020-08-02 11:41:29深入序列化和反序列化原理一个问题引发的思考什么是序列化和反序列化序列化 一个问题引发的思考 下面是一个简单的socket通信demo。 通信数据类: package com.zwx.serialize.demo; public class SocketUser { ... -
Java序列化的几种方式以及序列化的作用
2016-07-14 00:00:05一.Java序列化的作用有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象。例如,有的时候我们想要把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,... -
go语言序列化及反序列化
2020-06-19 22:56:45参考文章: ...特别好的文章:理解 Go 中的 JSON ...一、序列化 GO提供了 Marshal 方法:Go Struct转换为JSON对象,函数签名: func Marshal(v interface{}) ([]byte, error) 举例: type Person struct { Name . -
高效的Java序列化(目前市场上几乎所有的序列化方式排排坐)
2018-09-10 12:22:39序列化漫谈 dubbo RPC是dubbo体系中最核心的一种高性能、高吞吐量的远程调用方式,我喜欢称之为多路复用的TCP长连接调用,简单的说: 长连接:避免了每次调用新建TCP连接,提高了调用的响应速度 多路复用:... -
@responseBody + 序列化
2020-09-13 20:13:15因为我在看序列化的时候,就在考虑,Spring中使用@responseBody的Json序列化,为啥不用最常用的Serializable序列化呢?直接转换为二进制流不好吗??? 先说@responseBody 1、@responseBody注解的作用是将... -
SpringBoot中RedisTemplate序列化的几种方式
2019-09-03 16:02:10SpringBoot中RedisTemplate序列化的几种方式 JdkSerializationRedisSerializer、StringRedisSerializer、Jackson2JsonRedisSerializer* -
序列化反序列化的几种方式
2013-11-22 00:03:20一、Binary方法进行序列化 1、首先。你被序列化的类需要用[Serializable]特性修饰,例如: [Serializable] public class AA { public int i = 5; } 2、那么你可以用如下方法序列化和反序列化: /// /... -
Json数据的序列化与反序列化的三种常用方法介绍
2018-11-06 17:17:49Json数据的序列化与反序列化的三种常用方法介绍 -
几种序列化协议的介绍
2018-03-28 09:12:08序列化概述 1.序列化定义 序列化(serialization)就是将对象序列化为二进制形式(字节数组),一般也将序列化称为编码(Encode),主要用于网络传输、数据持久化等; 反序列化(deserialization)则是将从...