C#中的二进制序列化(真的,WYSIWYG序列化)

时间:2011-06-18 16:01:47

标签: c# .net serialization .net-4.0 binary-serialization

(对于WYSIWYG,我的意思是决定写 ,而不是微软或谷歌的人)(好的......从技术上讲,我没有做出任何决定......几年前编程的人决定了,而且我只能问我要跳多少钱。

我今天觉得有点蠢,但我已经失去了两个小时寻找解决方案:-(。

现在...

我有二进制协议。它是基于C的,所以它就像查看一个C结构,其中定义了机器的字节顺序(幸运的是它与“本地”字节顺序相同),定义了各种类型的大小,定义了数据结构对齐,结构的布局是定义的,字符串是固定的字符数组,在一个已知的编码...一切都定义了!当您使用unsafe struct时,一切都与C#[(LayoutKind.Explicit)]非常相似,而您对使用数组的fixed修饰符并不十分挑剔。现在我需要在C#中对它进行序列化/反序列化...我环顾四周但是我找不到任何东西......我错过了什么?在你提问之前,我知道BinaryFormatter,但它对我来说不够WYSIWYG ...... BinaryFormatter实现了它的格式化语言。是的,我知道BitConverter(以及它没有为big-endian实现转换器这一事实),但它不是一个“完整”的解决方案。它只是“基础”乐器。我知道BinaryWriter / BinaryReader,但它们似乎不支持不是byte[]char[]的数组,而且它们似乎无法“pad”写入一个数组(你有一个5个元素byte[]数组,你需要把它写成10个元素byte[]数组,因为你使用的格式需要它...你必须写执行此操作的代码行)

计划B(但可能甚至是计划Z)是为每个类创建一个阴影unsafe struct,一个IWysiwygSerializable接口,包含两个方法(ReadWrite)和在每个类中实现接口(写入将填充unsafe struct并将其写入输出流,读取将执行相反的操作)(或者我甚至可以直接在BitConverter中执行Read {1}}和Write没有使用struct,但对于数组来说,这有点困难)

谢谢!

3 个答案:

答案 0 :(得分:3)

我为声明性二进制序列化编写了一个相当简单但可扩展的框架。我在工作中广泛使用它,并且总是发现它节省了大量的精力:

binaryserializer.com

答案 1 :(得分:2)

不要使用BinaryFormatter。而是使用BinaryWriter和BinaryReader以您想要的确切顺序将要写入磁盘的确切字节写入磁盘。如果没有按照你喜欢的方式处理数组,那么你只需要自己遍历数组。为了使外观更清晰,你可以编写一个扩展方法来进行循环。

答案 2 :(得分:0)

(请注意,这或许可以被视为广告,但“产品”是开源的MIT许可,甚至其他引用的“产品”是开源的MIT许可)(请注意,我是广告“产品的作者”) “和另一个引用的”产品“)

没有任何“好”的解决方案,所以我做了我的:-)我必须创建一个库来创建库:FluentSerializer。该库可用于创建您希望如何序列化二进制数据的“描述”。此描述以流利的符号书写。您可以(通过我编写的其他库,FluentStatement)在您的流畅描述中包含所有常用语句,例如whileiffor ...(明确使用即使有一个流利的表示法)。然后将您的描述编译为表达式树,然后编译为一组动态方法(序列化,反序列化和大小(序列化数据))。

测试类的序列化程序的一小部分样本

/// <summary>
/// 
/// </summary>
public class Serializer : ISerializer<MyClass, EmptyParameters>
{
    #region ISerializer<MyClass,EmptyParameters> Members

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public Expression<Serializer<MyClass, EmptyParameters>> GetSerializer()
    {
        return (op, obj, par) => Statement.Start(fl => fl
            .Serialize(obj.Version)

            // Static objects can be serialized/deserialized.
            .Serialize(MyClass.StaticReadonlyInts1, typeof(FixedLength<>))

            // So can readonly collections.
            .Serialize(obj.ReadonlyInts1, typeof(FixedLength<>))

            // Both array and List<> (and Dictionary<,>, and SortedDictionary<,>, and
            // many other types of collections)
            ////.Serialize(obj.ReadonlyList1)
            .Serialize(obj.ReadonlyList1, typeof(VariableLengthByte<>))

            ////// Readonly fields can be serialized/deserialized.
            ////// Sadly you can't Dump() serializers that replace read only fields
            ////// (replace is the keyword here, readonly int X is a no-no, 
            ////// readonly List<int> X is a yes, readonly int[] X is a yes if it's 
            ////// FixedLength<>.
            ////.Serialize(obj.ReadonlyInt1)

            .Serialize(obj.Bool1)
            .Serialize(obj.Int2)

            // This will be serialized/deserialized only if obj.Version != 0
            // It's only an example of what you can do. You can use the full power of
            // FluentStatement, and remember that if instead of EmptyParameters you
            // had used another class as the parameters, you could have manipulated it
            // through the par object, so par.Version for example.
            .If(obj.Version != 0, fl.Serialize(obj.Int3))

            // This if(s) depend on the operation that is being done
            // (serialization/deserialization/size)
            .If(op == Operation.Serialize, fl.Serialize(obj.Int2))
            .If(op == Operation.Deserialize, fl.Serialize(obj.Int3))

            .Serialize(obj.Short1)

            // Tuples are supported.
            .Serialize(obj.Tuple1)

            // Arrays need to have the length prepended. There are helpers for this.
            // The default helper can be specified in the Serializer<T> constructor and
            // will be used if the field serializer isn't specified.
            ////.Serialize(obj.Ints1)

            // Or you can specify it:
            .Serialize(obj.Ints2, typeof(VariableLengthByte<>))
            .Serialize(obj.Ints3, typeof(VariableLengthByte<int[]>))

            // Nullable types are supported
            .Serialize(obj.NullableInt1, typeof(Nullable<int>))
            ////.Serialize(obj.NullableInt2)

            // But note that you could even use the Optional<> with value types,
            // usefull for example if you have to use a modifier that is a class
            // (like VariableLengthInt32 for example)
            .Serialize(obj.NullableInt1, typeof(Optional<int>))
            .Serialize(obj.NullableInt2, typeof(Optional<>))

            // So are "optional" objects (fields that can be null)
            // (Note that here if we wanted to specify the helper, we would have
            // to use typeof(Optional<VariableLengthByte<int>>)
            .Serialize(obj.OptionalInts1, typeof(Optional<VariableLengthInt32<int[]>>))
            .Serialize(obj.OptionalInts2, typeof(Optional<>))
            .Serialize(obj.OptionalList1, typeof(Optional<VariableLengthInt32<List<int>>>))
            .Serialize(obj.OptionalList2, typeof(Optional<>))

            // You can serialize a DateTime as the full .NET value
            .Serialize(obj.DateTime1)

            // Or, for example, as an Unix datetime (32 or 64 bits)
            .Serialize(obj.DateTime2, typeof(UnixDateTime<int>))

            .Serialize(obj.Float1)
            .Serialize(obj.Double1)
            .Serialize(obj.Decimal1)
            .Serialize(obj.TimeSpan1)

            // For strings it's a little more complex. There are too many combinations 
            // of possible formats (encoding x string length * (use char or byte length))
            // At this time there isn't any helper for C strings (null terminated strings).
            // You have to "manually" register you string formats.
            .Serialize(obj.String1, typeof(Program.MyUtf8VariableLengthInt32String))
            .Serialize(obj.String2, typeof(Program.MyAsciiVariableLengthInt32String))
            .Serialize(obj.String3, typeof(Program.MyUnicodeVariableLengthInt32String))

            // Chain serializing the base class can be done in this way
            .Serialize(obj, typeof(MySimpleClass))

            // This is only to make it easy to add new serialization fields. The last ) is
            // "attached" to the .Empty and doesn't need to be moved.
            .Empty());
    }

    #endregion
}

显然,只有在必须序列化/反序列化大量数据时,此库才是好的。如果您只有一个对象可以序列化/反序列化,BinaryReader / BinaryWriter可能就足够了(正如我在原始问题和Fantius的回答中所建议的那样)。