skip to Main Content

I am using newtonsoft json to convert some custom classes into payloads for web requests.
We are using a hashing system to ensure data concurrency.

I am using SerializeObject with custom settings:
JsonConvert.SerializeObject(_report,settings);

This is the settings that I am using:

public static JsonSerializerSettings GetJsonSettings(){
    JsonSerializerSettings settings = new JsonSerializerSettings();
    settings.DefaultValueHandling = DefaultValueHandling.Include;
    settings.MissingMemberHandling = MissingMemberHandling.Ignore;
    settings.NullValueHandling = NullValueHandling.Ignore;
    settings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
    settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    return settings;
}

When the serializer converts the object any floats are displayed with decimal points, including whole number floats. On the backend when js is reading the json payload when it converts floats to the js numbers any whole number floats lose their decimal point and trailing zeros:

This is one of the classes that is being serialized:

public class FlightLogEntry {
    public long timestamp;
    public Vector3 position;
    public float speedX;
    public float speedZ;
    public float speedMagnitude;
    public float battery;

    public FlightLogEntry() {

    }
}

1.02 => 1.02

Result of serialize in the program(C#):

{
    "timestamp": 230,
    "position": {
        "x": -15.07,
        "y": 2.009453,
        "z": -71.97
    },
    "speedX": 0.0,
    "speedZ": 0.0,
    "speedMagnitude": 0.0,
    "battery": 1379.98
}

1.0 => 1

Result of JSON.stringify(body) on the server(JS):

{
    "timestamp": 230,
    "position": {
        "x": -15.07,
        "y": 2.009453,
        "z": -71.97
    },
    "speedX": 0,
    "speedZ": 0,
    "speedMagnitude": 0,
    "battery": 1379.98
}

This was breaking our hash comparison checks, so what I did was make a custom converter to convert the floats on my end to trim the .0 on whole number floats. This corrected the hash mismatch but has introduced an issue on my end where when I write a backup to a file with the whole numbers trimmed, I cannot read them back as floats. They deserializer wants to read them as integers and throws an error.

Q: Is there a better way to handle the float 1.0 => 1 issue with js number?

Q: Is there a way to discern the "target" value in the ReadJson method of custom converters?

  • For example: If my json has a integer, but the target object class has a float, can I detect that a float is needed then make the cast manually in the JsonConverter.ReadJson method?

2

Answers


  1. I think you can convert floats without decimal part to integers. This way you you JavaScript json will be the same as c# json string.You can try this custom converter

    var json = JsonConvert.SerializeObject(_report, new FloatJsonConverter());
    
    public class FloatJsonConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            JObject o = JObject.FromObject(value);
            var floats = o.DescendantsAndSelf()
                        .Where(x => x.Type == JTokenType.Float)
                        .Select(x => (JProperty) x.Parent)
                        .ToList();
                        
            for (var i = 0; i < floats.Count(); i++)
            {
                var val = (float)floats[i];
                var intVal = (float)Math.Truncate(val);
                var decVal = Math.Abs((val - intVal));
                if (decVal <= 0.0000000000001) floats[i].Value = Convert.ToInt32(intVal);
            }
            o.WriteTo(writer);
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
        }
    
        public override bool CanConvert(Type objectType)
        {
            return true;
        }
    
        public override bool CanRead
        {
            get { return false; }
        }
    }
    

    you don’t a special converter to deserialize int to float, since Newtonsoft.Json does it automatically.

    Login or Signup to reply.
  2. I would argue the client and server needs to work on either the transmitted data, or the parsed objects.

    The server could just hash the received json to catch any transmission errors, before actually doing any de serialization. Ofc, the transmission layer should already handle consistency, so I’m not sure how useful this would be.

    Or both the server and client could to use a serialization method that is consistent to ensure the actual objects are the same. It should be possible to use json for this, but you would need to ensure the server and client use the exact same library, version of said library, culture, and settings. Json is not great for this since it is intended to be human readable, and humans usually do not care about things like 0 vs 0.0, or whitespace.

    I would think some sort of binary serialization would be less fragile, so that would probably be my preferred approach. You could use binaryWriter/reader and do manual serialization, that way you have full control. Or use something like protobuf .net and accept some risk that future versions might serialize messages slightly differently.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search