Custom Deserialization for a Specific Type (System.Version)

Hi,

I have a scenario where I am getting an exception when trying to process a message which contains a property of type System.Version.

NServiceBus.MessageDeserializationException: An error occurred while attempting to extract logical messages from incoming physical message 767ceb00-9113-4b3d-a7fc-b0dc00bdea72 —> System.ArgumentOutOfRangeException: Version’s parameters must be greater than or equal to zero. Parameter name: revision

I know why this is happening, in my eyes it is a bit of a shortcoming with .NET. The Version class defaults any Version components not known to -1. So if I have a Version string of “1.2.3” it get’s serialized as {‘Version’:{‘Major’:1,‘Minor’:2,‘Build’:3,‘Revision’:-1} .However, if you explicitly set a version part to -1, the class throws an exception and this is what is happening on deserialization.

Although this is not an NServiceBus issue, I would like to solve it in NServiceBus so it handles this scenario gracefully. We are currently using NewtonsoftSerializer for all serialization/deserialization. My first thought was to write a custom serializer that extends this class so it would try and not pass a -1 to the Version constructor. But because NewtonsoftSerializer inherits SerializationDefinition and not IMessageSerializer as I originally thought, I think I would have to create a class that is essentially a copy + paste of the private class JsonMessageSerializer and then implement my custom functionality on top of it. This is when I stopped and thought that things were getting a little complicated.

The other options I considered and decided against:

  • Using a message mutator - This could change any version components from -1 to 0, but that isn’t really correct. If a version component isn’t set, then it shouldn’t be included in my opinion.
  • Not using the System.Version class at all and passing “MajorVersion”, “MinorVersion” as separate integer properties etc. While we try to not to pass complex objects in messages to keep coupling to a minimum, I think passing a system class like this is not unreasonable.

So I am wondering, have I misunderstood how serialization/deserialization works in NSB or is there a solution I am overlooking?

Thanks,
Ian

Hi @igobl

First, I think you should be able to get this done by registering a custom converter and and pass that in your own custom newtonsoft configuration that you can pass to the serializer API. I’m not certain though as I’m not that familiar with the newtonsoft serializer itself.

Second, may I ask how you are using System.Version? Might it be possible to use SemanticVersion from the NuGet.Versioning package? This also would allow versions like 1.2.3-alpha.4. You would still require a custom converted though as that gets serialized as:

{"Major":1,"Minor":2,"Patch":3,"ReleaseLabels":["alpha","4"],"Release":"alpha.4","IsPrerelease":true,"HasMetadata":false,"Metadata":""}

Hi @ramonsmits,

Thanks for to pointing me in the direction of the custom converters, I didn’t know they existed but it turns out to be exactly what I needed. And even better, Newtonsoft have an existing VersionConverter to handle Serializing the System.Version class perfectly.

Might it be possible to use SemanticVersion from the NuGet.Versioning package?

Again, TIL this exists :smiley: For context we are using System.Version to track the code versions of clients that are placing orders in our system. I think our versioning is all purely numeric but I’ll check it out outside of this thread if SemanticVersion is more appropriate for our use case.

Anyway thanks for the info,
Ian

1 Like