A .NET serialisation library where the same function can both serialize and deserialize (i.e. Serdes = SERialize-DESerialize)
$ dotnet add package SerdesNetA .NET serialisation library where the same function can both serialize and deserialize (i.e. Serdes = SERialize-DESerialize)
The basic usage pattern is:
class SomeObject
{
public int Field1;
public string Field2;
public ushort Field3;
}
static SomeObject Serdes(SomeObject existing, ISerdes s) // This method handles both serialization and deserialization.
{
existing ??= new SomeType(); // If we're reading then typically 'existing' will be null, when writing it will be the object to write.
// When writing, each of these will basically be equivalent to
// "existing.Field1 = existing.Field1", but with the side-effect
// of serializing the value to the underlying stream.
// When reading, the values passed in will just be the defaults for the type and the return value will be the deserialized value.
existing.Field1 = s.Int32(nameof(existing.Field1), existing.Field1);
existing.Field2 = s.NullTerminatedString(nameof(existing.Field2), existing.Field2);
existing.Field3 = s.UInt16(nameof(existing.Field3), existing.Field3);
return existing;
}
The serdes methods like Int32(string name, int value), UInt16(string name, ushort value) etc should maintain these invariants:
value (i.e. serializing an object should not change it).value should be ignored and the return value should be the deserialized value.name parameter is only used to identify the field when using an annotation serdes for debugging. For the basic reader/writer serializers it is ignored.In cases where using the same code for reading and writing is impractical the flags can be checked to see if we are reading or writing. For example:
static SomeObject Serdes(SomeObject existing, ISerdes s)
{
if (s.IsReading)
{
// Don't need to use 'existing' at all when reading
var result = new SomeObject();
result.Field1 = s.Int32(nameof(result.Field1), 0);
result.Field2 = s.NullTerminatedString(nameof(result.Field2), "");
result.Field3 = s.UInt16(nameof(result.Field3), 0);
return result;
}
else
{
// Don't care about any return value when writing
if (existing == null) throw new ArgumentNullException(nameof(existing));
s.Int32(nameof(existing.Field1), existing.Field1);
s.NullTerminatedString(nameof(existing.Field2), existing.Field2);
s.UInt16(nameof(existing.Field3), existing.Field3);
return existing;
}
}
ISerdes for reading and writing with a stream.ISerdes which represents a non-existent file. Will error on most calls.