怎样处理从C#序列化浮点数到JavaScript中的数字

huangapple go评论69阅读模式
英文:

How to handle serializing floats from C# into numbers in Javascript

问题

我正在使用Newtonsoft JSON将一些自定义类转换为Web请求的有效负载。我们使用哈希系统确保数据的并发性。

我使用了自定义设置的SerializeObject:

JsonConvert.SerializeObject(_report, settings);

这是我使用的设置:

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;
}

当序列化程序转换对象时,任何浮点数都会显示小数点,包括整数浮点数。在后端,当JavaScript读取JSON有效负载并将浮点数转换为JavaScript数字时,任何整数浮点数都会失去其小数点和尾随零。

这是一个正在被序列化的类之一:

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

在程序中序列化的结果(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

在服务器(JS)上使用JSON.stringify(body)的结果:

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

这破坏了我们的哈希比较检查,因此我做了一个自定义转换器,在我的端上将浮点数转换为修剪整数浮点数的形式。这纠正了哈希不匹配,但在我的端上引入了一个问题,即当我将整数浮点数的备份写入文件时,我无法将它们读回作为浮点数。反序列化程序想要将它们读取为整数并抛出错误。

问题1:是否有更好的处理浮点数1.0 => 1与JS数字的方法?

问题2:在自定义转换器的ReadJson方法中是否有一种方法可以辨别“目标”值?

  • 例如:如果我的JSON有一个整数,但目标对象类具有浮点数,我能否检测到需要浮点数,然后在JsonConverter.ReadJson方法中手动进行转换?
英文:

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?

答案1

得分: 1

我认为你可以将没有小数部分的浮点数转换为整数。这样,你的 JavaScript JSON 将与 C# JSON 字符串相同。你可以尝试使用这个自定义转换器:

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("不必要,因为 CanRead 是 false。该类型将跳过该转换器。");
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override bool CanRead
    {
        get { return false; }
    }
}

你不需要特殊的转换器来将整数反序列化为浮点数,因为 Newtonsoft.Json 会自动执行这个操作。

英文:

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 =&gt; x.Type == JTokenType.Float)
					.Select(x =&gt; (JProperty) x.Parent)
					.ToList();
					
		for (var i = 0; i &lt; floats.Count(); i++)
		{
			var val = (float)floats[i];
			var intVal = (float)Math.Truncate(val);
			var decVal = Math.Abs((val - intVal));
			if (decVal &lt;= 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(&quot;Unnecessary because CanRead is false. The type will skip the converter.&quot;);
	}

	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.

答案2

得分: 0

我会认为客户端和服务器需要在传输的数据或解析的对象上进行工作。

服务器可以只需对接收到的JSON数据进行哈希处理,以捕获任何传输错误,然后再进行反序列化。当然,传输层应该已经处理了一致性的问题,所以我不确定这样做有多大用处。

或者,服务器和客户端都可以使用一种一致的序列化方法,以确保实际的对象相同。理论上可以使用JSON来实现这一点,但您需要确保服务器和客户端使用完全相同的库、库的版本、文化和设置。JSON 不太适合这个目的,因为它旨在供人类阅读,而人类通常不关心诸如 00.0 或空白符之类的细微差别。

我认为某种二进制序列化可能会更加稳定,因此这可能是我首选的方法。您可以使用 BinaryWriter/Reader 进行手动序列化,这样您可以完全掌握控制权。或者使用类似 Protobuf .NET 这样的工具,尽管可能要承担一些未来版本可能会稍有不同的风险。

英文:

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.

huangapple
  • 本文由 发表于 2023年2月23日 23:21:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75546831.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定