1
votes

It seems that there is a bug in the ServiceStack method TypeSerializer.DeserializeFromString regarding nullable types

Is this a known issue? Is there a fix or a workaround?

Consider the following code which includes a JsConfig scope that enforces exception throwing on any deserialization error:

public class MeasurementsData
{
    public int? FlowLeft { get; set; }
    public int? FlowRight { get; set; }
    public Single? RatioLeft { get; set; }
    public Single? RatioRight { get; set; }
}
 .
 .
 .

var originalData = "{FlowRight:970045,RatioRight:null}";
object afterConversionData = null;

try
{
    using (var scope = JsConfig.BeginScope())
    {
        scope.ThrowOnDeserializationError = true;
        afterConversionData =TypeSerializer.DeserializeFromString(originalData.ToString() ,typeof(MeasurementsData));
    }

}
catch (System.Runtime.Serialization.SerializationException ex)
{

    Logger.Warn("Service exception", ex);
}

The json inside the varaible originalData causes an exception (detailed below) "Failed to set property 'RatioRight' with 'null'". This happens despite 'RatioRight' is a 'nullable single' and 'null' is a valid value.

Removing scope.ThrowOnDeserializationError = true "works" but is not an option for us, since we need validation of the data received on the server side. I'll explain, if we set ThrowOnDeserializationError to false (which is the default), an error will not be thrown in this specific case but it would allow sending a json like this one: {FlowRight:970045,RatioRight:fdfd} the result of DeserializeFromString would not throw an exception although 'fdfd' is not a a nullable single. What would happen is that, the exception would be handled internally and null would be set instead, causing the server to not be aware that the data sent is invalid and would later cause saving of null values instead of the values already in the db (as I mentioned, not acceptable for us)

The Exception:

Source: ServiceStack.Text

Exception: Failed to set property 'MeasurementData' with '{FlowRight:970045,RatioRight:null}' InnerException: Failed to set property 'RatioRight' with 'null'

StackTrace: at ServiceStack.Text.Common.DeserializeTypeRefJsv.StringToType(TypeConfig typeConfig, String strType, EmptyCtorDelegate ctorFn, Dictionary'2 typeAccessorMap) at ServiceStack.Text.Common.DeserializeType'1.<>c__DisplayClass3.b__2(String value) at ServiceStack.Text.TypeSerializer.DeserializeFromString(String value, Type type) at Site.Service.Services.DataDomainService.Any(SaveDataDomainRequest request) in D:\Service\API Services\Domain\Services\DataDomainService.cs:line 93

1

1 Answers

0
votes

ServiceStack.Text's TypeSerializer uses the JSV Format which uses CSV Escaping so the null value is instead treated as a "null" string which can't be converted to a number, causing the Error.

To leave the default value it should be emitted from the JSV, e.g:

var originalData = "{FlowRight:970045}";

If you were instead looking for a JSON Serializer you should use JsonSerializer, e.g:

var afterConversionData = JsonSerializer.DeserializeFromString(
    originalData.ToString(), typeof(MeasurementsData));

Which can be rewritten more succinctly using Extension methods, e.g:

var object = originalData.ToString().FromJson<MeasurementsData>();