跳转至

IDL 对象与结构序列化

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/serializing-idl-objects-and-structures

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_BASE64ZLIB_COMPRESSZLIB_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相对于其他语言提供的灵活运行时行为,但有时我希望能有更多控制。你觉得呢?

循环引用 - 它们是什么以及如何解决? 机器学习 – 简化版