Better attributes for configuring object de-/serialization
Created by: RayKoopa
The BinaryMemberAttribute currently handles a lot of different aspects of object member serialization. It should be split up into the following attributes to allow easier and better extensible configuration and also shorten the code required to use the attributes:
-
Rename *DataFormatto just*Codingto further shorten the lengthy enumeration names. -
Replace OffsetOrigin.CurrentwithOrigin.AddandOffsetOrigin.BeginwithOrigin.Setto clarify purpose and shorten names. Default toOrigin.Begin. AllowOrigin.Alignto useAlign()method. -
Rename ByteOrder.BigEndiantoEndian.BigandEndian.LittleEndiantoEndian.Littleto further shorten the lengthy enumeration names. Apply similar naming toByteConverterinstances. -
Rename IBinaryConvertertoIDataConverter. -
DataClassAttribute: Represents the previousBinaryObjectAttribute. -
DataMemberAttribute: Used to explicitly mark a member to be read or written, in case the owning instance is annotated withDataClassAttribute.Explicit. -
DataBooleanAttribute: Configures BooleanCoding forboolmembers. -
DataDateTimeAttribute: Configures DateTimeCoding forDateTimemembers. -
DataEnumAttribute: Configures strict parsing ofEnummembers. -
DataStringAttribute: Configures StringCoding, Encoding and expected string length forstringmembers. -
DataConverterAttribute: Configures using an IDataConverter instance instead of the other features. -
DataArrayAttribute: Configures the number of elements to be read forIEnumerablemembers. Also allows passing astringto invoke an instance method or use another member of the instance for retrieving the actual size. -
DataOffsetAttribute: Configures theOriginmodification to apply to the current stream position. -
DataEndianAttribute: Configures theEndianto use, if not the default one. -
DataArrayAttributecan be combined withDataBooleanAttribute,DataDateTimeAttribute,DataEnumAttribute,DataStringAttribute,DataConverterAttribute,DataOffsetAttributeorDataByteOrderAttribute(the converter is invoked as many times as specified by the array length). -
DataConverterAttributecannot be combined withDataBooleanAttribute,DataDateTimeAttribute,DataEnumAttribute,DataStringAttributeorDataByteOrderAttribute. (? Maybe it can, and this information is passed to the interface again).
Additionally for structs and classes:
-
DataOffsetStartAttribute: Apply position modification before starting to read / write the instance. -
DataOffsetEndAttribute: Apply position modification after reading / writing the instance.
This results in the following breaking changes:
-
Remove BinaryMemberAttribute. -
Move the de-/serialization classes / interfaces into a new namespace, preferrably Syroot.BinaryData.Serialization. RenameIBinaryConvertertoIDataConverter. -
Clean up the IDataConverterinterface accordingly.
Practical example with X11 structures:
using Syroot.BinaryData;
using Syroot.BinaryData.Serialization;
namespace X11
{
public struct Screen
{
public Window Root; // uint
public Colormap DefaultColorMap; // uint
public uint WhitePixel;
public uint BlackPixel;
[DataEnum(true)] public Event CurrentInputMasks; // enum
public ushort WidthInPixels;
public ushort HeightInPixels;
public ushort WidthInMillimeters;
public ushort HeightInMillimeters;
public ushort MinInstalledMaps;
public ushort MaxInstalledMaps;
public VisualID RootVisual; // uint
[DataEnum(true)] public BackingStores BackingStores; // enum
[DataBoolean(BooleanCoding.Byte)] public bool SaveUnders;
public byte RootDepth;
public byte AllowedDepthsLength;
[DataArray(nameof(AllowedDepthsLength))] public IList<Depth> AllowedDepths;
}
public struct Depth
{
public byte DepthValue;
[DataOffset(Origin.Add, 1)] // 1 unused byte
public ushort VisualsLength;
[DataArray(nameof(VisualsLength))] public IList<VisualType> Visuals;
}
[DataOffsetEnd(Origin.Add, 4)] // 4 unused bytes at end
public struct VisualType
{
public VisualID VisualID; // uint
[DataEnum(true)] public VisualTypeClass Class; // enum
public byte BitsPerRgbValue;
public ushort ColormapEntries;
public uint RedMask;
public uint GreenMask;
public uint BlueMask;
}
}
Extreme example with string array read behind unused bytes (nice new line wrapping makes this logical):
public struct Potato
{
[DataByteOrder(ByteOrder.Big)] public ushort Age;
[DataOffset(Origin.Align, 4)] // Pad to next 4 byte boundary
[DataArray(3)] [DataString(StringCoding.Raw, 5, Encoding.ASCII)] public IList<string> ThreeFiveCharLongStrings;
}