【发布时间】:2010-10-20 16:57:05
【问题描述】:
我知道就几种分布式技术(例如 RPC)而言,使用了“编组”一词,但不明白它与序列化有何不同。他们不是都把物体变成了一系列的比特吗?
相关:
【问题讨论】:
标签: serialization terminology marshalling rpc
我知道就几种分布式技术(例如 RPC)而言,使用了“编组”一词,但不明白它与序列化有何不同。他们不是都把物体变成了一系列的比特吗?
【问题讨论】:
标签: serialization terminology marshalling rpc
编组和序列化在远程过程调用的上下文中松散地是同义词,但在语义上是不同的。
特别是,编组是关于从这里到那里获取参数,而序列化是关于将结构化数据复制到或从原始形式(如字节流)复制。从这个意义上说,序列化是执行封送处理的一种手段,通常实现按值传递的语义。
对象也可以通过引用来编组,在这种情况下,“在线”数据只是原始对象的位置信息。但是,这样的对象可能仍然可以进行值序列化。
正如@Bill 所提到的,可能还有其他元数据,例如代码库位置甚至对象实现代码。
【讨论】:
I、大小写更改等进行装饰。
两者都有一个共同点 - 即序列化一个对象。序列化用于传输对象或存储它们。但是:
所以序列化是编组的一部分。
CodeBase 是告诉 Object 的接收者在哪里可以找到该对象的实现的信息。任何认为它可能将对象传递给以前可能没有见过它的另一个程序的程序都必须设置代码库,以便接收者可以知道从哪里下载代码,如果它没有本地可用的代码。接收者将在反序列化对象后,从中获取代码库并从该位置加载代码。
【讨论】:
invokeAndWait 和 Forms 的 Invoke,它们在不涉及序列化的情况下编组对 UI 线程的同步调用。
the implementation of this object 是什么意思?你能举一个Serialization和Marshalling的具体例子吗?
来自Marshalling (computer science) 维基百科文章:
术语“marshal”在 Python 标准库1 中被认为与“serialize”同义,但在 Java 相关的 RFC 2713 中这些术语不是同义词:
“编组”一个对象意味着以这样一种方式记录其状态和代码库,当编组的对象被“解组”时,可以获得原始对象的副本,可能通过自动加载类定义物体。您可以编组任何可序列化或远程的对象。编组类似于序列化,除了编组还记录代码库。编组与序列化的不同之处在于编组特别对待远程对象。 (RFC 2713)
“序列化”一个对象意味着将其状态转换为字节流,以便字节流可以转换回对象的副本。
因此,编组还将对象的代码库保存在字节流中,除了其状态。
【讨论】:
我认为主要区别在于编组据说还涉及代码库。换句话说,您将无法将对象编组和解组为不同类的状态等效实例。
序列化只是意味着您可以存储对象并重新获得等效状态,即使它是另一个类的实例。
话虽如此,它们通常是同义词。
【讨论】:
封送处理是指将函数的签名和参数转换为单字节数组。 专门用于 RPC。
序列化更多时候是指将整个对象/对象树转换为字节数组 编组将序列化对象参数,以便将它们添加到消息中并通过网络传递。 *序列化也可用于存储到磁盘。*
【讨论】:
字节流 - 流是数据序列。输入流 - 从源读取数据。输出流 - 将数据写入目的地。 Java 字节流用于逐字节(一次 8 位)执行输入/输出。字节流适用于处理二进制文件等原始数据。 Java 字符流用于一次执行 2 个字节的输入/输出,因为字符是使用 Java 中的 Unicode 约定存储的,每个字符有 2 个字节。当我们处理(读/写)文本文件时,字符流很有用。
RMI(远程方法调用) - 一种 API,它提供了一种在 java 中创建分布式应用程序的机制。 RMI 允许对象调用在另一个 JVM 中运行的对象的方法。
Serialization 和 Marshalling 都用作同义词。以下是一些差异。
序列化 - 对象的数据成员被写入二进制形式或字节流(然后可以写入文件/内存/数据库等)。一旦将对象数据成员写入二进制形式,就无法保留有关数据类型的信息。
编组 - 对象被序列化(以二进制格式的字节流),附加数据类型+代码库,然后传递远程对象(RMI)。编组会将数据类型转换为预先确定的命名约定,以便可以根据初始数据类型对其进行重构。
所以序列化是编组的一部分。
CodeBase 是告诉 Object 的接收者在哪里可以找到该对象的实现的信息。任何认为它可能将对象传递给以前可能没有见过它的另一个程序的程序都必须设置代码库,以便接收者可以知道从哪里下载代码,如果它没有本地可用的代码。接收者将在反序列化对象后,从中获取代码库并从该位置加载代码。 (复制自@Nasir 答案)
序列化几乎就像对象使用的内存的愚蠢内存转储,而编组存储有关自定义数据类型的信息。
在某种程度上,序列化通过按值传递的实现来执行编组,因为没有传递数据类型的信息,只是将原始形式传递给字节流。
如果流从一个操作系统传输到另一个操作系统,如果不同的操作系统具有不同的表示相同数据的方式,则序列化可能会遇到一些与大端、小端相关的问题。另一方面,编组非常适合在操作系统之间迁移,因为结果是更高级别的表示。
【讨论】:
编组是告诉编译器如何在另一个环境/系统上表示数据的规则; 例如;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
如您所见,两个不同的字符串值表示为不同的值类型。
序列化 只会转换对象内容,而不是表示(将保持不变)并遵守序列化规则(导出或不导出什么)。例如,私有值不会被序列化,公共值是,对象结构将保持不变。
【讨论】:
以下是两者的更具体示例:
序列化示例:
#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# 的角度来看,它有点像一个黑盒子。
另一个有趣的概念是,您可能了解如何编写代码,以及知道如何执行指令的计算机,因此作为程序员,您正在有效地从大脑中整理出您希望计算机做什么的概念到程序图像。如果我们有足够好的编组器,我们可以只考虑我们想要做什么/改变,程序会改变这种方式而无需在键盘上打字。所以,如果你有办法在你真正想写分号的几秒钟内将所有物理变化存储在你的大脑中,你可以将这些数据编组为一个信号以打印一个分号,但这是一个极端。
【讨论】:
编组通常在相对密切相关的进程之间进行;序列化不一定有这种期望。因此,例如,当在进程之间编组数据时,您可能希望仅发送一个 REFERENCE 来恢复可能昂贵的数据,而对于序列化,您希望将其全部保存,以便在反序列化时正确重新创建对象。
【讨论】:
我对编组的理解与其他答案不同。
序列化:
利用约定生成或重新水化对象图的线格式版本。
编组:
利用映射文件生成或重新水化对象图的线格式版本,以便可以自定义结果。该工具可能从遵守约定开始,但重要的区别在于自定义结果的能力。
合同优先开发:
编组在合同优先开发的背景下很重要。
【讨论】:
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).
序列化与编组
问题:对象属于某个进程(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
【讨论】:
编组实际上使用序列化过程,但主要区别在于它在序列化中仅对数据成员和对象本身进行序列化而不是签名,但在编组对象 + 代码库(其实现)中也会将其转换为字节。
Marshalling 是使用 JAXB 将 java 对象转换为 xml 对象以便可以在 Web 服务中使用的过程。
【讨论】:
将它们视为同义词,两者都有一个生产者将内容发送给消费者......最后,实例的字段被写入字节流,而另一端则相反并使用相同的实例。
NB - java RMI 还支持传输接收方缺少的类...
【讨论】: