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
290 stars 62 forks source link

Conditional field serialization - ISO8583 #187

Closed nullpainter closed 2 years ago

nullpainter commented 2 years ago

I'm wishing to use BinarySerializer to both serialize and deserialize ISO8583 messages. These messages contain bitmaps to indicate whether specific fields are present or not.

I have created custom IBinarySerializable bitmap properties, which work well for serialization of the bitmaps themselves.

The difficulty I'm facing is that ISO8583 messages inherently have a dynamic length. If a property isn't present, it shouldn't consume bytes in the message.

SerializeWhenAttribute won't work for me, as this refers to the state of other serialized properties.

Setting a property to null just results in n 0 bytes written, where n is the value defined in FieldLength.

All of my fields are currently custom. I have tried leaving FieldLength off entirely and conditionally serializing based on another property, but then buffers aren't correctly sized inside IBinarySerializable.

I also tried setting a ConverterType for FieldLength, however the associated IValueConverter is only called on deserialization, not serialization.

In summary:

  1. Is there a way for to not serialize a field but to also not serialize FieldLength bytes?
  2. If not, is there a way to programmatically set FieldLength inside IBinarySerializable?
nullpainter commented 2 years ago

I anticipate a similar issue with the secondary bitmap and LLVAR and LLLVAR data types, where the first 2-3 bytes contain the field length. Is there a reason why ConverterType on FieldLength isn't being called on serialization? I think this may solve all of my problems:

  1. For optional fields, I can set the field length to zero if the field is null
  2. For LLVAR and LLLVAR, I can set the field length based on the property type and initial bytes
  3. The optional secondary bitmap can be serialized only if the first byte of the primary bitmap is 1
nullpainter commented 2 years ago

Further digging suggests that ConverterType is called on serialization, but only when the path to bind to is numeric.

In my case, I don't care so much about the path so had set it to nameof(PrimaryBitmap) just for the sake of something to set it to.

As an experiment, I added an integer field in my model to bind to. Using this, I managed to get my custom converter to be called correctly and my fields being conditionally serialized - however I have a new problem in that I have an integer field being serialized that I don't want to.

What I'm after is something like this. Here, DataElement is a custom attribute.

[FieldOrder(5)]
[FieldLength(ConverterType = typeof(OptionalFieldConverter))]
[DataElement(DataElements.DE7, length: 10)]
public BcdInt? TransmissionDateTime { get; set; }

And the same pattern for LLLVAR:

[FieldOrder(10)]
[FieldLength(ConverterType = typeof(DynamicFieldConverter))]
[DataElement(DataElements.DE125, maxFieldLength: 256)] 
public LllVar MessageInformationCode { get; set; }
nullpainter commented 2 years ago

After all of that, I have created my own serializer for this.

Whereas BinarySerializer could work, ISO8859 uses a sufficiently different approach that makes it less error-prone to do the serialization myself. For example, all integers and longs are BCD, all fields of a particular ISO data type will have the same length and can be used in multiple models, all LLVAR fields will have the same length calculation etc.

So, feel free to address my questions if you wish - but I will probably not use BinarySerializer for this particular project. Until next time!

jefffhaynes commented 2 years ago

Sorry I'm just getting to this. Maybe I'm missing something, but can you simply wrap the length and value in a field that is conditionally serialized? That format looks fiendishly complicated so it may be that there is something breaking there!

nullpainter commented 2 years ago

It's been a while so I can't remember the exact issues I was facing, although it may be related to the length being dynamic. My custom serializer works a charm and has been a blessing in disguise, as it's allowed me to hook in custom validations, pretty printing etc.

It's a pity I can't release it as it's commercial. The format is indeed fun, but straightforward once you get your head around it!

jefffhaynes commented 2 years ago

Glad to hear it!