许多运行时值可以被序列化或反序列化,使用 haxe.Serializer 和 haxe.Unserializer 类。都支持两种用法:
创建一个实例,并不断的调用 serialize / unserialize 方法来处理多个值。 调用它们的静态 run 方法来 序列化/反序列化 一个单独的值。
下面的例子演示了第一种用法:
import haxe.Serializer;
import haxe.Unserializer;
class Main {
static function main() {
var serializer = new Serializer();
serializer.serialize("foo");
serializer.serialize(12);
var s = serializer.toString();
trace(s); // y3:fooi12
var unserializer = new Unserializer(s);
trace(unserializer.unserialize()); // foo
trace(unserializer.unserialize()); // 12
}
}
序列化的结果(这里存储在局部变量 s)是一个 String,并且可以被任意甚至远程地传递。它的格式以 序列化格式(第10.8.1节)描述。
- null
- Bool, Int 和 Float (包括无穷值和 NaN)
- String
- Date
- haxe.io.Bytes (编码为 base64)
- Array 和 List
- haxe.ds.StringMap, haxe.ds.IntMap 和 haxe.ds.ObjectMap
- 匿名结构
- Haxe 类实例(并不是原生的)
- 枚举实例
序列化可以以两种方式配置。对于一个静态变量,可以被设置来改变所有 haxe.Serializer 实例,一个成员变量可以被设置来影响一个特定实例:
- USE_CACHE,userCache : 如果为 true,重复的结构或类/枚举实例被参照序列化。这可以避免递归数据的无限循环更长的序列化时间。默认,对象缓存是禁用的;然而字符串总是被缓存。
- USE_ENUM_INDEX,useEnumIndex : 如果为 true ,枚举构造函数被它们的索引序列化而不是它们的名字。这可以使结果字符串更短,但是如果 枚举构造函数在反序列化之前被插入到这个类型,将会打断。这个行为默认是禁止的。
如果序列化结果被存储,之后使用于反序列化,必须注意当使用类和枚举实例时要保持兼容性。之后重要的是准确理解反序列化如何实现的。
- 做反序列化的地方类型必须在运行时可以获得的。如果无用代码消除被激活,只是通过序列化使用的类型可能会被删除。
- 每个 Unserializer 都有一个成员变量 resolver ,用于通过名字解析类和枚举。Unserializer 一经创建,它被设置为 Unserializer.DEFAULT_RESOLVER 。它和实例成员都可以被设置为一个定制 分析器。
- 类使用 resolver.resolveClass(name) 通过名字解析。实例然后被使用 Type.createEmptyInstance 创建,这意味着 这个类构造函数没有被调用。最终,实例字段根据序列化的值被设置。
- 枚举使用 resolver.resolveEnum(name)通过名字及诶系。枚举实例然后被使用 Type.createEnum创建,如果可用,则使用序列化的参数值。如果构造函数参数由于序列化被改变,结果是未指定的。
如果一个类定义了成员方法 hxSerialize,这个方法被序列化器调用,允许对类的自定义序列化。同样,如果一个类定义了成员方法 hxUnserialize,它被反序列化器调用:
import haxe.Serializer;
import haxe.Unserializer;
class Main {
var x:Int;
var y:Int;
static function main() {
var s = Serializer.run(new Main(1, 2));
var c:Main = Unserializer.run(s);
trace(c.x); // 1
trace(c.y); // -1
}
function new(x, y) {
this.x = x;
this.y = y;
}
@:keep
function hxSerialize(s:Serializer) {
s.serialize(x);
}
@:keep
function hxUnserialize(u:Unserializer) {
x = u.unserialize();
y = -1;
}
}
在这个例子中,我们决定要忽略成员变量 y的值,并且不序列化它。相反,我们在 hxUnserialize中默认它为 -1 。两个方法都使用 @:keep 元数据注解,以防止无用代码消除删除它们,因为它们在代码中从未恰当的引用。
查看 Serializer 和 Unserializer API 文档了解详细内容。
每个支持的值被转换为一个不同前缀字符、后跟需要的数据。
- null: n
- Int: 0为z,或者 i 后跟整数显示(例如 i456)
- Float:
- NaN :k
- 负无穷 :m
- 正无穷 :p
- 有限的浮点数 : d 后跟浮点数显示(如d1.45e-8)
- Bool :true 为 t,false 为 f
- String: y 后跟url编码的字符串长度,然后是冒号 : 和url编码(如 y10:hi%20there,“hi there”)
- 名称-值 对: 一个序列化的字符串表示名称后跟序列化的值
- 结构: o 后跟 名称-值对的列表,以g 终止?(例如,oy1:xi2y1:kng,{x:2, k:null})
- List: l 后跟序列化的项的列表,后跟h(例如,lnnh ,两个null值的 List)
- Array: a 后跟序列化的项的列表,后跟h。对于多个连续的 null 值, 使用u 后跟null的数量(例如 ai1i2u4i7ni9h ,[1, 2, null, null, null, null, 7, null, 9])
- Date: v 后跟 date 自己(例如 v2010-01-01 12:45:10)
- haxe.ds.StringMap: b 后跟 名-值对,后跟 h(例如,by1:xi2y1:knh,{"x"=> 2, "k"=> null})
- haxe.ds.IntMap: q 后跟键值对,后跟h。每个键表示为 :(例如,q:4n:5i45:6i7h ,{4=>null, 5=> 45, 6=>7})
- haxe.ds.ObjectMap: M 后跟序列化的值对表示键跟值,后跟h
- haxe.io.Bytes: s 后跟 base64编码字节长度,然后冒号 : ,和A-Za-z0-9% 表示的字节码(例如,s3:AAA ,等于0的2字节,s10:SGVsbG8gIQ ,haxe.io.Bytes.ofString("Hello !"))
- exception: x 后跟异常值
- 类实例: c 后跟序列化的类名,后跟字段的名-值对,后跟g(例如,cy5:Pointy1:xzy1:yzg ,new Point(0, 0))(有两个整数字段 x 和 y)
- 枚举实例 (通过名称): w 后跟序列化的枚举名称,后跟序列化的构造函数名,后跟冒号 : ,后跟参数的数量,后跟参数的值(例如,wy3:Fooy1:A:0 ,Foo.A (无参数),wy3:Fooy1:B:2i4n ,Foo.B(4, null))
- 枚举实例(通过索引): j 后跟序列化的枚举名称,后跟冒号 : ,后跟构造函数的索引(从0开始),后跟冒号 :, 后跟参数的数量,后跟参数的值(例如,jy3:Foo:0:0 , Foo.A (无参数), jy3:Foo:1:2i4n ,Foo.B(4, null))
- 缓存引用:
- String: R 后跟在字符串缓存中相应的索引(例如,R456)
- class, enum 或 structure : r 后跟对象缓存中的相应索引(如,r42)
- 自定义: C 后跟类名,后跟自定义序列化数据,后跟 g
缓存的元素和枚举构造函数从0开始索引。