简单序列化/反序列化实现

时间:2013-09-18 11:53:30

标签: c# serialization xml-serialization protobuf-net

我想了解如何在不使用框架的情况下实现Serialize / Deserialize XmlSerializer的。序列化应该支持图形。 我遇到了protobuf-net看起来像是诀窍,但它是一个非常复杂的项目,我有点迷失了。我还发现了NetSerializer,但它看起来并不支持图形。 有一个更简单的项目吗?

1 个答案:

答案 0 :(得分:0)

使用DataContractSerializer,例如:

[DataContract(IsReference = true)]
class Foo
{
    [DataMember(Order = 1)]
    public Foo Parent { get; set; }
}

应该有效。对于protobuf-net,非常相似的东西应该起作用:

[ProtoContract(AsReferenceDefault = true)]
class Foo
{
    [ProtoMember(1)]
    public Foo Parent { get; set; }
}

来自评论:

  

我的问题不是如何使用它。它是如何从后面实施的

要从头开始实现,这主要涉及的是:当您第一次看到对象时(通常使用 引用 相等,而不是Equals / GetHashCode覆盖或==重载),您发明了一个新的唯一标识符。此标识符可以是Guid,随机数,或更简单地...... 123,......所以当您看到一个对象时第一次,你说“这是一个新的对象,身份1,内容{...}”;然后每当你再次看到它时,你只需说“引用1”。

如果您要定位核心.NET,可以使用ObjectIDGenerator

using System.Runtime.Serialization;
static class Program
{
    static void Main()
    {
        var idgen = new ObjectIDGenerator();
        object foo = new object(), bar = new object(), blap = foo;
        Write(idgen, foo);
        Write(idgen, bar);
        Write(idgen, blap); // note this is the same object as foo
    }
    static void Write(ObjectIDGenerator idgen, object obj)
    {
        bool isNew;
        long id = idgen.GetId(obj, out isNew);
        System.Console.WriteLine("{0}, {1}", id, isNew);
    }
}

带输出:

1, True
2, True
1, False

但是因为protobuf-net针对的是一系列并不总是可用的平台,所以它在内部实现了这一点。作为一个实现细节,对于protobuf-net,这是NetObjectCache.AddObjectKey方法(可能应该被称为GetObjectKey,考虑它)。实现它的确切方式取决于目标框架,但首选实现只是Dictionary<object,int>使用自定义比较器来确保多态GetHashCode() / {{1}不使用:

Equals

然后只是private sealed class ReferenceComparer : System.Collections.Generic.IEqualityComparer<object> { public readonly static ReferenceComparer Default = new ReferenceComparer(); private ReferenceComparer() {} bool System.Collections.Generic.IEqualityComparer<object>.Equals(object x, object y) { return x == y; // ref equality } int System.Collections.Generic.IEqualityComparer<object>.GetHashCode(object obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); } } / TryGetValue的情况,即

Add

请注意,它会使稍微更复杂,因为protobuf-net对字符串的处理方式不同 - 不是引用,而是if (!objectKeys.TryGetValue(value, out index)) index = -1; ... if (!(existing = index >= 0)) { index = list.Add(value); // <=== creates a new identity ... objectKeys.Add(value, index); // <=== adds to the dictionary } s - 以确保如果两个字符串的值相同,但是是不同的字符串引用,它使用相同的引用并且只存储字符串一次。相比之下,string / BinaryFormatter在此处做出另一种选择,以保留重复字符串上的引用标识。您可以使用以下代码在前面的代码中看到:

ObjectIdGenerator

有关信息,写入身份+有效负载与引用的详细信息位于Write(idgen, new string('a', 3)); Write(idgen, new string('a', 3)); ,但请注意,这是特定于实现/格式的。但基本上它使用不同的数字键来表示“这是一个新对象/引用”以“重用现有对象”,通过:

BclHelpers.WriteNetObject

并且只为新对象写入对象 payload