繁体   English   中英

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

[英]Simple Serialize/Deserialize implementation

我想了解如何不使用框架XmlSerializer来实现序列化/反序列化。 序列化应支持图形。 我碰到了protobuf-net ,看起来像是在解决问题,但这是一个非常复杂的项目,我对此有些迷惑。 我还找到了NetSerializer,但它看起来不支持图形。 有一个更简单的项目吗?

使用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() / 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一种情况,即

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
}

请注意,它变得稍微复杂一点,因为protobuf-net将字符串以不同的方式对待-不是作为引用 ,而是作为string s-以确保如果两个字符串的值相同,但是字符串引用不同,则它使用相同的引用并且仅存储一次字符串。 相比之下, BinaryFormatter / ObjectIdGenerator在此处做出其他选择,以保留重复字符串上的引用身份。 您可以使用以下代码在前面的代码中看到此内容:

Write(idgen, new string('a', 3));
Write(idgen, new string('a', 3));

有关信息,在BclHelpers.WriteNetObject中编写了identity + payload vs reference的详细信息,但请注意,这是特定于实现/格式的。 但基本上,它通过以下方式使用不同的数字键来指示“这是新对象/引用”以“重用现有对象”:

bool existing;
int objectKey = dest.NetCache.AddObjectKey(value, out existing);
ProtoWriter.WriteFieldHeader(existing
    ? FieldExistingObjectKey : FieldNewObjectKey, WireType.Variant, dest);
ProtoWriter.WriteInt32(objectKey, dest);
if (existing)
{
    writeObject = false;
}

并且只为新对象写入对象有效负载

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM