Skip to content

Latest commit

 

History

History
135 lines (103 loc) · 6.52 KB

10.8.序列化.md

File metadata and controls

135 lines (103 loc) · 6.52 KB

10.8.序列化

许多运行时值可以被序列化或反序列化,使用 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 文档了解详细内容。

10.8.1.格式化序列化

每个支持的值被转换为一个不同前缀字符、后跟需要的数据。

  • 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开始索引。