Serialization和Marshaling有什么区别?

时间:2009-04-20 23:26:53

标签: serialization terminology marshalling rpc

我知道在几种分布式技术(例如RPC)方面,使用术语“Marshaling”但不理解它与序列化的区别。它们不是都将对象转换为一系列位?

相关:

What is Serialization?

What is Object Marshalling?

13 个答案:

答案 0 :(得分:337)

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

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

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

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

答案 1 :(得分:159)

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

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

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

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

答案 2 :(得分:89)

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

  

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

     

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

     

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

因此,除了状态之外,编组还会保存字节流中对象的代码

答案 3 :(得分:18)

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

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

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

答案 4 :(得分:17)

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

序列化更常用于将整个对象/对象树转换为字节数组 Marshaling将序列化对象参数,以便将它们添加到邮件中并通过网络传递。 *序列化也可用于存储到磁盘。*

答案 5 :(得分:9)

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

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

因为您可以看到两个不同的字符串值表示为不同的值类型。

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

答案 6 :(得分:4)

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

答案 7 :(得分:4)

以下是两个更具体的例子:

序列化示例:

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

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

Marshaling不需要涉及动态分配,它也可以只是结构之间的转换。例如,您可能有一对,但该函数期望该对的第一和第二个元素是相反的;你铸造/记忆一对到另一对不会做的工作因为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引擎打印出一个&#34; c字符串&#34;为你,但你可以要求它为你打印一个包裹的c字符串。或者,如果要在Lua或Python运行时中从JavaScript运行时打印字符串。它们都是字符串,但通常在没有编组的情况下不会相处。

我最近遇到的一个烦恼是,JScript数组将C#编组为&#34; __ ComObject&#34;,并且没有记录的方式来使用此对象。我可以找到它所在的地址,但我真的不知道其它任何事情,所以真正弄清楚它的唯一方法是以任何可能的方式戳它,并希望找到有用的信息。因此,使用更友好的界面(如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);
        }
    }
}
  

以上打印&#34; __ ComObject&#34;,从C#的角度来看,它有点像黑盒子。

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

答案 8 :(得分:3)

基础优先

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

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


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

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

enter image description here

编组-将对象序列化(二进制格式的字节流),并附加数据类型+ Codebase,然后传递 Remote Object(RMI)。编组会将数据类型转换为预定的命名约定,以便可以相对于初始数据类型进行重构。 enter image description here

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

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

串行化几乎就像对象使用的内存的愚蠢内存转储,而 Marshalling 则存储有关自定义数据类型的信息。

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

如果流正在从一个操作系统转到另一操作系统,而不同的操作系统具有不同的表示相同数据的方式,则串行化可能会遇到与大端,小端有关的问题。另一方面,编组在OS之间进行迁移非常好,因为结果是更高级别的表示。

答案 9 :(得分:2)

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

<强>序列化

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

<强>编组:

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

合同优先发展:

在合同首次开发的背景下,编组很重要。

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

答案 10 :(得分:1)

序列化与编组

问题:对象属于某个进程(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

答案 11 :(得分:0)

将它们视为同义词,两者都有一个生产者将东西发送给消费者......最后,实例的字段被写入字节流,而另一端则以相同的实例反向出现。

NB - java RMI还包含对传输收件人丢失的类的支持...

答案 12 :(得分:0)

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

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