【问题标题】:What is the difference between Serialization and Marshaling?序列化和封送处理有什么区别?
【发布时间】:2010-10-20 16:57:05
【问题描述】:

我知道就几种分布式技术(例如 RPC)而言,使用了“编组”一词,但不明白它与序列化有何不同。他们不是都把物体变成了一系列的比特吗?

相关:

What is Serialization?

What is Object Marshalling?

【问题讨论】:

    标签: serialization terminology marshalling rpc


    【解决方案1】:

    编组和序列化在远程过程调用的上下文中松散地是同义词,但在语义上是不同的。

    特别是,编组是关于从这里到那里获取参数,而序列化是关于将结构化数据复制到或从原始形式(如字节流)复制。从这个意义上说,序列化是执行封送处理的一种手段,通常实现按值传递的语义。

    对象也可以通过引用来编组,在这种情况下,“在线”数据只是原始对象的位置信息。但是,这样的对象可能仍然可以进行值序列化。

    正如@Bill 所提到的,可能还有其他元数据,例如代码库位置甚至对象实现代码。

    【讨论】:

    • 有没有同时表示序列化和反序列化的词?需要为这些方法的接口命名。
    • @raffian,你的意思是由经过序列化和反序列化的对象实现的接口,还是由负责管理进程的对象实现的接口?我建议的关键词分别是“Serializable”和“Formatter”;根据需要使用前导 I、大小写更改等进行装饰。
    • @JeffreyHantin 我的意思是负责管理流程的对象;我现在正在使用 ISerializer,但这只对了一半:)
    • @raffian 在电信领域,我们称序列化和反序列化“SerDes”或“serdes”的组件,通常发音为sir-dez 或sir-deez,具体取决于偏好。我想它的结构类似于“调制解调器”(即“调制器-解调器”)。
    • @naki 它是全行业的——如果您查看高速 FPGA 数据表,他们会提到 SERDES 功能,尽管这些功能都相当现代,可以追溯到 1990 年代。 Google NGrams 表明它在 1980 年代变得更加流行,尽管我确实在 1970 的 IBM 数据表中找到了一个实例
    【解决方案2】:

    两者都有一个共同点 - 即序列化一个对象。序列化用于传输对象或存储它们。但是:

    • 序列化:当你序列化一个对象时,只有该对象内的成员数据被写入字节流;不是那个代码 实际实现了对象。
    • 编组:当我们谈论将对象传递给远程对象(RMI)时使用术语编组。在 Marshalling Object is serialized(member data is serialized) + Codebase 已附加。

    所以序列化是编组的一部分。

    CodeBase 是告诉 Object 的接收者在哪里可以找到该对象的实现的信息。任何认为它可能将对象传递给以前可能没有见过它的另一个程序的程序都必须设置代码库,以便接收者可以知道从哪里下载代码,如果它没有本地可用的代码。接收者将在反序列化对象后,从中获取代码库并从该位置加载代码。

    【讨论】:

    • +1 用于定义 CodeBase 在此上下文中的含义
    • 确实会发生没有序列化的编组。请参阅 Swing 的 invokeAndWait 和 Forms 的 Invoke,它们在不涉及序列化的情况下编组对 UI 线程的同步调用。
    • "不是实际实现对象的代码":是指类方法吗?或者这是什么意思。你能解释一下吗?
    • the implementation of this object 是什么意思?你能举一个SerializationMarshalling的具体例子吗?
    • 编组无序列化发生在某些情况下,例如当函数调用在线程模型之间传输控制流时(例如,在共享线程池和单固定-线程库)在单个进程中。这就是为什么我说它们在 RPC 的上下文中是松散的同义词。
    【解决方案3】:

    来自Marshalling (computer science) 维基百科文章:

    术语“marshal”在 Python 标准库1 中被认为与“serialize”同义,但在 Java 相关的 RFC 2713 中这些术语不是同义词:

    “编组”一个对象意味着以这样一种方式记录其状态和代码库,当编组的对象被“解组”时,可以获得原始对象的副本,可能通过自动加载类定义物体。您可以编组任何可序列化或远程的对象。编组类似于序列化,除了编组还记录代码库。编组与序列化的不同之处在于编组特别对待远程对象。 (RFC 2713)

    “序列化”一个对象意味着将其状态转换为字节流,以便字节流可以转换回对象的副本。

    因此,编组还将对象的代码库保存在字节流中,除了其状态。

    【讨论】:

    • 你的意思是一个对象,如果未序列化,可以只有状态,不会有任何代码库,即它的任何函数都不能被调用,它只是一个结构化的数据类型。而且,如果对同一个对象进行编组,那么它将具有其代码库以及结构,并且一旦可以调用其函数?
    • “代码库”并不真正意味着“代码”。来自“代码库如何工作”(goo.gl/VOM2Ym) 代码库非常简单,就是使用 RMI 的远程类加载语义的程序如何找到新类。当对象的发送者将该对象序列化以传输到另一个 JVM 时,它会使用称为代码库的信息来注释序列化的字节流。这个信息告诉接收者在哪里可以找到这个对象的实现。存储在代码库注释中的实际信息是一个 URL 列表,可以从中下载所需对象的类文件。
    • @Neurone 该定义特定于 Jini 和 RMI。 “代码库”是一个通用术语。 en.wikipedia.org/wiki/Codebase
    • @BilltheLizard 是的,但是因为您在谈论Java中的编组,所以说序列化和编组之间的区别是“编组除了保存对象的状态之外还保存了对象的代码”是错误的,并且它导致了bjan的问题。除了对象状态之外,编组还保存“代码库”。
    【解决方案4】:

    我认为主要区别在于编组据说还涉及代码库。换句话说,您将无法将对象编组和解组为不同类的状态等效实例。

    序列化只是意味着您可以存储对象并重新获得等效状态,即使它是另一个类的实例。

    话虽如此,它们通常是同义词。

    【讨论】:

    • 你的意思是一个对象,如果未序列化,只能有状态,不会有任何代码库,即不能调用它的任何函数,它只是一种结构化数据类型。而且,如果对同一个对象进行编组,那么它将具有其代码库以及结构,并且可以调用其函数?
    【解决方案5】:

    封送处理是指将函数的签名和参数转换为单字节数组。 专门用于 RPC。

    序列化更多时候是指将整个对象/对象树转换为字节数组 编组将序列化对象参数,以便将它们添加到消息中并通过网络传递。 *序列化也可用于存储到磁盘。*

    【讨论】:

      【解决方案6】:

      基础知识优先

      字节流 - 流是数据序列。输入流 - 从源读取数据。输出流 - 将数据写入目的地。 Java 字节流用于逐字节(一次 8 位)执行输入/输出。字节流适用于处理二进制文件等原始数据。 Java 字符流用于一次执行 2 个字节的输入/输出,因为字符是使用 Java 中的 Unicode 约定存储的,每个字符有 2 个字节。当我们处理(读/写)文本文件时,字符流很有用。

      RMI(远程方法调用) - 一种 A​​PI,它提供了一种在 java 中创建分布式应用程序的机制。 RMI 允许对象调用在另一个 JVM 中运行的对象的方法。


      SerializationMarshalling 都用作同义词。以下是一些差异。

      序列化 - 对象的数据成员被写入二进制形式或字节流(然后可以写入文件/内存/数据库等)。一旦将对象数据成员写入二进制形式,就无法保留有关数据类型的信息。

      编组 - 对象被序列化(以二进制格式的字节流),附加数据类型+代码库,然后传递远程对象(RMI)。编组会将数据类型转换为预先确定的命名约定,以便可以根据初始数据类型对其进行重构。

      所以序列化是编组的一部分。

      CodeBase 是告诉 Object 的接收者在哪里可以找到该对象的实现的信息。任何认为它可能将对象传递给以前可能没有见过它的另一个程序的程序都必须设置代码库,以便接收者可以知道从哪里下载代码,如果它没有本地可用的代码。接收者将在反序列化对象后,从中获取代码库并从该位置加载代码。 (复制自@Nasir 答案)

      序列化几乎就像对象使用的内存的愚蠢内存转储,而编组存储有关自定义数据类型的信息。

      在某种程度上,序列化通过按值传递的实现来执行编组,因为没有传递数据类型的信息,只是将原始形式传递给字节流。

      如果流从一个操作系统传输到另一个操作系统,如果不同的操作系统具有不同的表示相同数据的方式,则序列化可能会遇到一些与大端、小端相关的问题。另一方面,编组非常适合在操作系统之间迁移,因为结果是更高级别的表示。

      【讨论】:

        【解决方案7】:

        编组是告诉编译器如何在另一个环境/系统上表示数据的规则; 例如;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
        

        如您所见,两个不同的字符串值表示为不同的值类型。

        序列化 只会转换对象内容,而不是表示(将保持不变)并遵守序列化规则(导出或不导出什么)。例如,私有值不会被序列化,公共值是,对象结构将保持不变。

        【讨论】:

          【解决方案8】:

          以下是两者的更具体示例:

          序列化示例:

          #include <stdio.h>
          #include <stdlib.h>
          #include <stdint.h>
          
          typedef struct {
              char value[11];
          } SerializedInt32;
          
          SerializedInt32 SerializeInt32(int32_t x) 
          {
              SerializedInt32 result;
              
              itoa(x, result.value, 10);
          
              return result;
          }
          
          int32_t DeserializeInt32(SerializedInt32 x) 
          {
              int32_t result;
              
              result = atoi(x.value);
              
              return result;
          }
          
          int main(int argc, char **argv)
          {    
              int x;   
              SerializedInt32 data;
              int32_t result;
              
              x = -268435455;
              
              data = SerializeInt32(x);
              result = DeserializeInt32(data);
              
              printf("x = %s.\n", data.value);
              
              return result;
          }
          

          在序列化中,数据以一种可以在以后存储和取消展平的方式展平。

          编组演示:

          (MarshalDemoLib.cpp)

          #include <iostream>
          #include <string>
          
          extern "C"
          __declspec(dllexport)
          void *StdCoutStdString(void *s)
          {
              std::string *str = (std::string *)s;
              std::cout << *str;
          }
          
          extern "C"
          __declspec(dllexport)
          void *MarshalCStringToStdString(char *s)
          {
              std::string *str(new std::string(s));
              
              std::cout << "string was successfully constructed.\n";
              
              return str;
          }
          
          extern "C"
          __declspec(dllexport)
          void DestroyStdString(void *s)
          {
              std::string *str((std::string *)s);
              delete str;
              
              std::cout << "string was successfully destroyed.\n";
          }
          

          (MarshalDemo.c)

          #include <Windows.h>
          #include <stdio.h>
          #include <stdlib.h>
          #include <stdint.h>
          
          int main(int argc, char **argv)
          {
              void *myStdString;
          
              LoadLibrary("MarshalDemoLib");
              
              myStdString = ((void *(*)(char *))GetProcAddress (
                  GetModuleHandleA("MarshalDemoLib"),
                  "MarshalCStringToStdString"
              ))("Hello, World!\n");
              
              ((void (*)(void *))GetProcAddress (
                  GetModuleHandleA("MarshalDemoLib"),
                  "StdCoutStdString"
              ))(myStdString);
          
              ((void (*)(void *))GetProcAddress (
                  GetModuleHandleA("MarshalDemoLib"),
                  "DestroyStdString"
              ))(myStdString);    
          }
          

          在编组中,数据不一定需要展平,但需要转换为另一种替代表示。所有的强制转换都是编组,但并非所有的编组都是强制转换。

          编组不需要涉及动态分配,它也可以只是结构之间的转换。例如,您可能有一对,但函数期望这对的第一个和第二个元素是相反的;您将一对转换/memcpy 对另一对将无法完成这项工作,因为 fst 和 snd 会被翻转。

          #include <stdio.h>
          
          typedef struct {
              int fst;
              int snd;
          } pair1;
          
          typedef struct {
              int snd;
              int fst;
          } pair2;
          
          void pair2_dump(pair2 p)
          {
              printf("%d %d\n", p.fst, p.snd);
          }
          
          pair2 marshal_pair1_to_pair2(pair1 p)
          {
              pair2 result;
              result.fst = p.fst;
              result.snd = p.snd;
              return result;
          }
          
          pair1 given = {3, 7};
          
          int main(int argc, char **argv)
          {    
              pair2_dump(marshal_pair1_to_pair2(given));
              
              return 0;
          }
          

          当您开始处理多种类型的标记联合时,编组的概念变得尤为重要。例如,您可能会发现很难让 JavaScript 引擎为您打印“c 字符串”,但您可以要求它为您打印包装好的 c 字符串。或者,如果您想在 Lua 或 Python 运行时中从 JavaScript 运行时打印字符串。它们都是字符串,但通常不经过编组就无法相处。

          我最近遇到的一个烦恼是 JScript 数组将 C# 编组为“__ComObject”,并且没有记录使用此对象的方法。我可以找到它的地址,但我真的不知道它的任何其他信息,所以真正弄清楚它的唯一方法是用任何可能的方式戳它,希望能找到关于它的有用信息。因此,使用更友好的界面(如 Scripting.Dictionary)创建新对象变得更加容易,将 JScript 数组对象中的数据复制到其中,并将该对象传递给 C# 而不是 JScript 的默认数组。

          (test.js)

          var x = new ActiveXObject('Dmitry.YetAnotherTestObject.YetAnotherTestObject');
              
          x.send([1, 2, 3, 4]);
          

          (YetAnotherTestObject.cs)

          using System;
          using System.Runtime.InteropServices;
          
          namespace Dmitry.YetAnotherTestObject
          {
              [Guid("C612BD9B-74E0-4176-AAB8-C53EB24C2B29"), ComVisible(true)]
              public class YetAnotherTestObject
              {
                  public void send(object x)
                  {
                      System.Console.WriteLine(x.GetType().Name);
                  }
              }
          }
          

          上面打印“__ComObject”,从 C# 的角度来看,它有点像一个黑盒子。

          另一个有趣的概念是,您可能了解如何编写代码,以及知道如何执行指令的计算机,因此作为程序员,您正在有效地从大脑中整理出您希望计算机做什么的概念到程序图像。如果我们有足够好的编组器,我们可以只考虑我们想要做什么/改变,程序会改变这种方式而无需在键盘上打字。所以,如果你有办法在你真正想写分号的几秒钟内将所有物理变化存储在你的大脑中,你可以将这些数据编组为一个信号以打印一个分号,但这是一个极端。

          【讨论】:

            【解决方案9】:

            编组通常在相对密切相关的进程之间进行;序列化不一定有这种期望。因此,例如,当在进程之间编组数据时,您可能希望仅发送一个 REFERENCE 来恢复可能昂贵的数据,而对于序列化,您希望将其全部保存,以便在反序列化时正确重新创建对象。

            【讨论】:

              【解决方案10】:

              我对编组的理解与其他答案不同。

              序列化:

              利用约定生成或重新水化对象图的线格式版本。

              编组:

              利用映射文件生成或重新水化对象图的线格式版本,以便可以自定义结果。该工具可能从遵守约定开始,但重要的区别在于自定义结果的能力。

              合同优先开发:

              编组在合同优先开发的背景下很重要。

              • 可以对内部对象图进行更改,同时保持外部接口随着时间的推移保持稳定。这样,所有服务订阅者都不必为每一个微不足道的更改而修改。
              • 可以将结果映射到不同的语言。例如,从一种语言 ('property_name') 到另一种语言 ('propertyName') 的属性名称约定。

              【讨论】:

              • // ,我可以知道更多关于“补水”的意思,在这个答案中,@JasperBlues?我猜这不仅仅是为了宇航员的食物。
              • @NathanBasanese 根据这个答案 - stackoverflow.com/a/6991192/5101816 - (重新)保湿的定义包含以下词语:Hydrating an object is taking an object that exists in memory, that doesn't yet contain any domain data ("real" data), and then populating it with domain data (such as from a database, from the network, or from a file system).
              【解决方案11】:

              序列化与编组

              问题:对象属于某个进程(VM)并且它的生命周期是相同的

              Serialisation - 将对象状态转换为字节流(JSON、XML...)以进行保存、共享、转换...

              Marshalling - 包含Serialisation + codebase。通常由Remote procedure call(RPC) -> Java Remote Method Invocation(Java RMI) 使用,您可以在其中调用托管在远程 Java 进程上的对象方法。

              codebase - 是指向class definition 的位置或 URL,ClassLoader 可以在其中下载它。 CLASSPATH[About] 是一个本地代码库

              JVM -> Class Loader -> load class definition
              
              java -Djava.rmi.server.codebase="<some_URL>" -jar <some.jar>
              

              非常简单的 RMI 图表

              Serialisation - state
              Marshalling - state + class definition
              

              Official doc

              【讨论】:

                【解决方案12】:

                编组实际上使用序列化过程,但主要区别在于它在序列化中仅对数据成员和对象本身进行序列化而不是签名,但在编组对象 + 代码库(其实现)中也会将其转换为字节。

                Marshalling 是使用 JAXB 将 java 对象转换为 xml 对象以便可以在 Web 服务中使用的过程。

                【讨论】:

                  【解决方案13】:

                  将它们视为同义词,两者都有一个生产者将内容发送给消费者......最后,实例的字段被写入字节流,而另一端则相反并使用相同的实例。

                  NB - java RMI 还支持传输接收方缺少的类...

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-09-21
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-06-14
                    • 2013-05-21
                    • 2016-03-27
                    • 1970-01-01
                    相关资源
                    最近更新 更多