序列化和封送处理之间的区别是什么?

我知道,就一些分布式技术(如RPC)而言,术语“编组”;使用了串行化,但不理解它与串行化的区别。它们不是都在把对象转换成一系列的比特吗?

相关:

什么是序列化?< / >

什么是对象封送?< / >

147278 次浏览

我认为主要的区别在于编组应该也涉及到代码库。换句话说,您将无法将对象编组或反编组到不同类的状态等效实例中。

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

也就是说,它们通常是同义词。

来自编组(计算机科学)维基百科的文章:

术语“马歇尔”;被认为是“序列化”的同义词;在Python标准库1中,但这些术语在与java相关的RFC 2713中不是同义词:

“;marshal"一个对象意味着以这样一种方式记录它的状态和代码库,即当被编组的对象被“解组”时,可以通过自动加载对象的类定义来获得原始对象的副本。您可以封送任何可序列化或远程的对象。编组与序列化类似,只不过编组也记录代码库。编组与序列化的不同之处在于编组专门处理远程对象。(RFC 2713)

“;serialize"对象意味着将其状态转换为字节流,使字节流可以转换回该对象的副本。

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

编组通常在相对紧密关联的进程之间进行;序列化不一定有这种期望。因此,当在进程之间编组数据时,例如,您可能希望仅发送一个引用到可能昂贵的数据以进行恢复,而使用序列化时,您可能希望保存所有数据,以便在反序列化时正确地重新创建对象。

在远程过程调用的上下文中,封送处理和序列化是松散同义的,但是由于意图不同,语义上是不同的。

特别地,封送处理是关于从这里到那里获取参数,而序列化是关于将结构化数据复制到字节流等基本形式或从中复制。在这个意义上,序列化是执行封送处理的一种方法,通常实现值传递语义。

也可以通过引用封送对象,在这种情况下,“在线上”的数据只是原始对象的位置信息。但是,这样的对象仍然可以接受值序列化。

正如@Bill提到的,可能会有额外的元数据,比如代码基位置,甚至是对象实现代码。

可以把它们看作同义词,它们都有一个生产者,把东西发送给消费者……在实例的结束字段被写入一个字节流,而另一端则将相同的实例反向和向上写入。

注意:java RMI还支持从接收端传输缺少的类…

封送是指将函数的签名和参数转换为单字节数组。

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

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

    当你序列化一个对象时,只有该对象中的成员数据被写入字节流;不是那个代码
  • 术语编组在我们讨论将对象传递给远程对象(RMI)时使用。在编组对象是序列化的(成员数据是序列化的)+代码基附加。

因此序列化是编组的一部分。

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

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

序列化:

利用约定制作或补充对象图的有线格式版本。

编组:

利用映射文件制作或补充对象图的有线格式版本,以便可以自定义结果。该工具可以从遵循约定开始,但重要的区别在于自定义结果的能力。

合同优先开发:

编组在合同优先开发的上下文中很重要。

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

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

[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;
}

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

编组演示:

(# EYZ0)

#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";
}

(# EYZ0)

#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数组marshal到c#为“__ComObject",并且没有文档化的方法来处理这个对象。我可以找到它的地址,但我真的不知道关于它的其他任何东西,所以真正弄清楚它的唯一方法是用任何可能的方式戳它,希望能找到关于它的有用信息。因此,它变得更容易创建一个新的对象与友好的接口,如脚本。字典,从JScript数组对象复制数据到它,并将该对象传递给c#,而不是JScript的默认数组。

(# EYZ0)

var x = new ActiveXObject('Dmitry.YetAnotherTestObject.YetAnotherTestObject');
    

x.send([1, 2, 3, 4]);

(# EYZ0)

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#的角度来看,这有点像一个黑盒子。

另一个有趣的概念是,你可能知道如何写代码,而计算机知道如何执行指令,因此作为一个程序员,你正在有效地将你想让计算机做的事情的概念从你的大脑中编组到程序图像中。如果我们有足够好的编组器,我们可以只考虑我们想要做什么/改变什么,程序就会这样改变,而不需要在键盘上输入。所以,如果你能有一种方法,在你真正想要写分号的那几秒钟里,把所有的物理变化存储在你的大脑里,你可以把数据编组成一个信号来打印一个分号,但这是一种极端。

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

编组是使用JAXB将java对象转换为xml对象的过程,以便可以在web服务中使用它。

基础知识第一

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

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


序列化编组都被松散地用作同义词。这里有一些区别。

对象的数据成员被写入二进制形式或字节流(然后可以写入文件/内存/数据库等)。一旦将对象数据成员写入二进制形式,就不能保留任何关于数据类型的信息。

enter image description here

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

enter image description here

因此序列化是编组的一部分。

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

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

在某种程度上,Serialization通过值传递的实现来执行封送,因为没有传递数据类型的信息,只是将原始形式传递到字节流。

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

序列化vs编组

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

Serialisation -转换对象状态字节流(JSON, XML…)保存,共享,转换…

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

codebase -是一个到class definition的位置或URL,它可以被ClassLoader下载。CLASSPATH<一口>[对]< /一口>相当于本地代码库

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

非常简单的RMI图

Serialisation - state
Marshalling - state + class definition

官方文件 .