IDL 对象与结构序列化
12782 给这篇文章评分:
5.0
IDL 对象与结构序列化
Jim Pendleton 2014年11月13日,星期四
在我最近的 IDL Data Point 博客中,我展示了一些示例代码,用于扩展 IDL_Variable 的静态方法以包含用户定义的方法,从而为大多数变量类型的数据序列化提供简化的语法。
具体示例展示了一种序列化IDL变量的技术,将它们转换为压缩的ASCII字节流,以便在某些协议下更容易传输,例如通过HTTP传输到在云上运行的 ENVI Services Engine 实例。
对象引用和结构数据类型被排除在其他变量表示的通用 IDL_Variable "超类" 定义的可扩展范围之外。
在本文中,我将简要讨论一种序列化对象和结构的机制。
我们无法追溯性地扩展IDL_Variable的静态方法来为对象和结构提供我们想要的简化语法。
解决这个问题的另一种方法是在一个新类中创建一对静态方法。一个方法将接收要序列化的变量的引用作为输入并返回序列化结果。第二个方法则执行逆操作。
用于压缩和解压缩的静态方法使用了先前文章中描述的相同技术,对于对象引用和结构之外的所有变量类型,调用 IDL_BASE64 和 ZLIB_COMPRESS 或 ZLIB_UNCOMPRESS。
对于它们,我们需要一个不同的解决方案。
FUNCTION jp_Serializer::Serialize, v, DIM = Dim, TYPECODE = TypeCode COMPILE_OPT STATIC dim = v.Dim typeCode = v.TypeCode IF (typeCode EQ 8 || typeCode EQ 11) THEN BEGIN ; TODO 结构体(8)和对象引用(11)如何处理? ENDIF ELSE BEGIN RETURN, IDL_BASE64(ZLIB_COMPRESS(v)) ENDELSE END
注意,命名空间划分要求使用不太可能冲突的标识符。这里我使用 "jp_" 作为前缀。
序列化的一个技巧是将变量存储到IDL SAVE文件中,通过 READ_BINARY 将文件内容读入 BYTARR,然后使用我们应用于其他数据的相同技术序列化该BYTARR。
tempFile = FILEPATH('temp.sav', /TMP) SAVE, v, /COMPRESS, FILE = tempFile b = READ_BINARY(tempfile, DATATYPE = 1) FILE_DELETE, tempfile RETURN, IDL_BASE64(ZLIB_COMPRESS(b))
注意:更健壮的实现会生成一个唯一的、未被使用的文件名,这或许是另一篇博客文章的主题。
以类似的方式,反序列化过程将解压缩的字节流写入文件,然后将数据作为SAVE文件读取。
tempFile = FILEPATH('temp.sav', /TMP) OPENW, lun, tempFile, /GET_LUN WRITEU, lun, ZLIB_UNCOMPRESS(IDL_BASE64(var), TYPE = 1) FREE_LUN, lun RESTORE, tempFile FILE_DELETE, tempFile RETURN, v
我们知道要返回的变量名为"v",因为在上面的序列化例程中它就是那样保存的。通过RESTORE操作,它已经"神奇地"出现在该例程的上下文中。
以下是两个静态方法都完全实现的类:
`FUNCTION jp_Serializer::Deserialize, var, DIM = dim, TYPECODE = typeCode
COMPILE_OPT STATIC
IF (typeCode EQ 8 || typeCode eq 11) THEN BEGIN
tempFile = FILEPATH('temp.sav', /TMP)
OPENW, lun, tempFile, /GET_LUN
WRITEU, lun, ZLIB_UNCOMPRESS(IDL_BASE64(var), TYPE = 1)
FREE_LUN, lun
RESTORE, tempFile
FILE_DELETE, tempFile
RETURN, v
ENDIF ELSE BEGIN
RETURN, ZLIB_UNCOMPRESS(IDL_BASE64(var), DIM = dim, TYPE = typeCode)
ENDELSE
END
FUNCTION jp_Serializer::Serialize, v, DIM = Dim, TYPECODE = TypeCode
COMPILE_OPT STATIC
dim = v.Dim
typeCode = v.TypeCode
IF (typeCode EQ 8 || typeCode EQ 11) THEN BEGIN
tempFile = FILEPATH('temp.sav', /TMP)
SAVE, v, /COMPRESS, FILE = tempFile
b = READ_BINARY(tempfile, DATATYPE = 1)
FILE_DELETE, tempfile
RETURN, IDL_BASE64(ZLIB_COMPRESS(b))
ENDIF ELSE BEGIN
RETURN, IDL_BASE64(ZLIB_COMPRESS(v))
ENDELSE
END
PRO jp_Serializer__Define
!null = {jp_Serializer, _ : 0B}
END`
以下是一个"常规"变量序列化的例子:
`IDL> b = bindgen(2,2,2)
IDL> b
0 1
2 3
4 5
6 7
IDL> s = jp_Serializer.Serialize(b, DIM=dim, TYPECODE=type)
IDL> s,dim,type
eJxiYGRiZmFlYwcAAAD//wMAAFwAHQ==
2 2 2
1`
反序列化遵循相同的模式:
`IDL> print, jp_Serializer.Deserialize(s, DIM=dim,TYPECODE=type)
0 1
2 3
4 5
6 7`
现在让我们看看对象引用会发生什么:
IDL> o = orb()
IDL> so = jp_Serializer.Serialize(o)
IDL> so.strlen()
9384
IDL> dso = jp_Serializer.Deserialize(so, TYPECODE=11)
IDL> dso
<ObjHeapVar28(ORB)>
IDL> xobjview, dso
请记住,当我们SAVE一个对象引用时,它会制作指定对象数据的副本。此外,它还会制作指定对象内引用的任何对象的副本,因此请谨慎使用此功能。
显然,在IDL会话之间传递序列化对象时,必须确保所需的类和结构定义在这些会话之间同步,并且在恢复反序列化文件之前执行类定义。你能想出一种序列化和反序列化可执行代码以确保兼容性的技术吗(假设是相同版本的IDL)?(提示:一种技术将涉及例程的SAVE文件,非常类似于我们上面处理数据文件的方式。)
为什么要使用静态方法?为什么不简单地按照传统意义创建函数?
通过将相关功能分组到一个类中,我可以轻松扩展功能,而不必担心覆盖其他函数。代码位于单个源文件中,便于维护。命名空间由类定义,提供了一定程度的冲突保护。
如果能有一种方法使方法"最终化",就像其他语言那样,运行时环境会禁止重新编译或子类化方法,那就太好了。这或许可以通过新的COMPILE_OPT来实现。尽管我经常利用IDL相对于其他语言提供的灵活运行时行为,但有时我希望能有更多控制。你觉得呢?