Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent undefined values from serializing to 0 for float values in .Net?

I have a rather large object class that is defined by a bunch of primitive attribute values (int, float, bool, string). I am getting the object from a client application as a json string which I deserialize into a C# .Net class so that I can save them to a SQL database. The problem I am having is the serializer is giving me a default value of 0 for float parameters which breaks my app as undefined values need to be handled differently than a value of 0. (Note: 0 values are acceptable if the user has defined them to be 0, but I can't assume that an undefined value is 0.)

I have literally hundreds of these primitive attributes, so I'm hoping there is a way to make this work globally instead of having to write custom attribute type objects.

Here's how I'm deserializing the JSON string into a C# object

using System.Web.Script.Serialization; // Note: used to deserialize JSON objects
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

RootObject obj = JsonConvert.DeserializeObject<RootObject>(JSONObjectFromClient);

And here's my object class

public class SeatDefinition
{
public string DefinitionID { get; set; }
public string r3_tolType { get; set; }
public float r3_value { get; set; } // Note: this could be 0, but it shouldn't be assumed to be 0 if undefined
public bool r3_verified { get; set; }
public float r4_minus { get; set; } // same here
public float r4_plus { get; set; } // and here
public string r4_tolType { get; set; }
public float r4_value { get; set; } //etc
public bool r4_verified { get; set; }
public float r5_minus { get; set; }
public float r5_plus { get; set; }
public string r5_tolType { get; set; }
public float r5_value { get; set; }
public bool r5_verified { get; set; }
// ... 400 more such attributes
}

Can anyone help?

EDIT 2016-01-05 11:38PM PST Turns out I'm a moron. The auto-magic of the deserializer will leave values as null if you state in the class definition that they should be nullable. All I needed to do to resolve my problem was change

public bool r3_verified { get; set; }

to

public bool? r3_verified { get; set; }

and I was left with null values for those that were not passed in as I needed.

Thank you @dbc for pointing me in the right direction.

like image 558
nickvans Avatar asked Dec 29 '25 18:12

nickvans


1 Answers

The most natural way to do this is to define your float properties as nullables:

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public float? r3_value { get; set; }

Setting NullValueHandling.Ignore prevents the property from being serialized to JSON when not present. It's also possible to do this by setting JsonSerializerSettings.NullValueHandling thus avoiding the need to add the attribute to each property.

Another possibility (which I don't really recommend) is to define a special "sentinal" constant value that indicates an undefined floating value; then set the attribute [DefaultValue(Constants.UninitializedFloat)] on each float to inform Json.NET of this default value; then set [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] to indicate that, when the property is missing from the JSON, the default value should be automatically assigned:

public static class Constants
{
    public const double UninitializedFloat = float.MinValue;

    public static bool IsUninitialized(this float value)
    {
        return value == UninitializedFloat;
    }
}

public class SeatDefinition
{
    [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
    [DefaultValue(Constants.UninitializedFloat)]
    public float r3_value { get; set; } // Note: this could be 0, but it shouldn't be assumed to be 0 if undefined
}

I don't recommend this because there's a small chance your sentinal value might not round-trip if accidentally serialized and deserialized. From the docs:

A value might not round-trip if a floating-point number is involved. A value is said to round-trip if an operation converts an original floating-point number to another form, an inverse operation transforms the converted form back to a floating-point number, and the final floating-point number is equal to the original floating-point number. The round trip might fail because one or more least significant digits are lost or changed in a conversion.

Thus your sentinal might get rounded slightly and appear to become an initialized value.

like image 61
dbc Avatar answered Dec 31 '25 06:12

dbc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!