626

我知道就几种分布式技术(例如 RPC)而言,使用了“编组”一词,但不明白它与序列化有何不同。它们不是都将对象转换为一系列位吗?

有关的:

什么是序列化?

什么是对象编组?

4

13 回答 13

480

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

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

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

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

于 2009-04-20T23:42:52.433 回答
262

两者都做一件事——序列化一个对象。序列化用于传输对象或存储它们。但:

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

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

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

于 2013-02-02T02:50:43.727 回答
112

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

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

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

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

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

于 2009-04-20T23:31:23.430 回答
20

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

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

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

于 2009-04-20T23:30:39.420 回答
19

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

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

于 2011-10-08T15:14:51.620 回答
17

基础第一

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

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


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

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

在此处输入图像描述

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

在此处输入图像描述

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

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

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

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

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

于 2020-01-02T06:31:33.040 回答
14

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

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

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

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

于 2015-03-12T17:54:36.347 回答
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# 的角度来看,它有点像一个黑盒子。

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

于 2017-09-10T06:41:39.027 回答
4

My understanding of marshalling is different to the other answers.

Serialization:

To Produce or rehydrate a wire-format version of an object graph utilizing a convention.

Marshalling:

To Produce or rehydrate a wire-format version of an object graph by utilizing a mapping file, so that the results can be customized. The tool may start by adhering to a convention, but the important difference is the ability to customize results.

Contract First Development:

Marshalling is important within the context of contract first development.

  • Its possible to make changes to an internal object graph, while keeping the external interface stable over time. This way all of the service subscribers won't have to be modified for every trivial change.
  • Its possible to map the results across different languages. For example from the property name convention of one language ('property_name') to another ('propertyName').
于 2014-06-25T00:14:18.297 回答
4

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

于 2009-04-20T23:32:45.843 回答
2

序列化与编组

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

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

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

codebaseclass definition- 是ClassLoader 可以下载的地方或 URL 。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

官方文档

于 2021-01-29T16:52:29.390 回答
1

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

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

于 2017-11-14T11:01:31.313 回答
0

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

注意 - java RMI 还包含对传输接收者缺少的类的支持......

于 2009-04-20T23:46:53.360 回答