jefffhaynes / BinarySerializer

A declarative serialization framework for controlling formatting of data at the byte and bit level using field bindings, converters, and code.
MIT License
292 stars 62 forks source link

Data validation / Error detection strategies #153

Open sanek2k6 opened 4 years ago

sanek2k6 commented 4 years ago

Hello,

Are there any existing strategies to detect parsing/deserialize errors and to catch at least the most obvious parsing errors?

For example, if I'm specifying that the FieldLength should be 4, but only 1 byte is available, that 1 byte is used and no error is thrown.

Is there an easy way to add custom validation on field-by-field basis (i.e. if you know a range that the valid value should be in)?

Thank you

jefffhaynes commented 4 years ago

I’ll have to look at it but that doesn’t sound quite right to me. I’m also in the midst of rewriting a section of the library that may address this. What is the underlying type of the field that you’re constraining?

sanek2k6 commented 4 years ago

Just a recent example of this that I've seen:

    [Serializable]
    public class UnixDateTime
    {
        [FieldOrder(0)]
        [FieldLength(4)]
        [SerializeAs(SerializedType.UInt4)]
        public uint TimeStamp { get; set; }

        [Ignore]
        public DateTimeOffset Value
        {
            get => DateTimeOffset.FromUnixTimeSeconds(TimeStamp);
            set => TimeStamp = (uint)value.ToUnixTimeSeconds();
        }
    }

    static void Main(string[] args)
    {
        var serializer = new BinarySerializer {Endianness = Endianness.Big};
        var unixDateTime = serializer.Deserialize<UnixDateTime>(new byte[] { 0xFF });

        // Invalid: There was no exception thrown and this outputs: FF000000
        Console.WriteLine(unixDateTime.TimeStamp.ToString("X08"));
    }
xchellx commented 2 years ago

As a workaround, you can follow a fail-fast system with MemberSerialized/MemberDeserialized events on your BinarySerializer where you can check each field as it is serialized/deserialized and throw when it is invalid by your checks.

EDIT: Realized BinarySerializer swallows exceptions sometimes. Instead, a workaround can have you collect exceptions in a List at MemberSerialized/MemberDeserialized then check if that list count is not 0 later on and throw as a AggregateException. Its not pretty but a good workaround for right now.