我需要反序列化原始二进制数据 (BinaryFormatter),然后序列化为 JSON(用于编辑),然后再次将其序列化回二进制。
显然,我输在了浮动上。原始浮点值0xF9FF4FC1
(大端,大致-12.9999933)四舍五入为0xF6FF4FC1
(-12.99999)当我序列化时原始二进制文件(正确数据和中间数据在内存中是1:1)JSON。我知道这并不是一个很大的损失,而且我知道浮动是有问题的,但由于以后可能出现不兼容问题,我想保持尽可能接近的精度。
有人用 JSON 解决过这个问题吗?如何强制它以最大精度写入浮点数?我已经尝试过将浮点数处理为十进制或双精度的内置选项,但不幸的是,输出没有区别,而且我无法更改目标值,因为当我进行二进制序列化时,它们仍然需要写为浮点数,因此会进行舍入无论在隐式转换期间。
我想要往返的包含浮动的特定类型是Vector2
from https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.cs https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.cs.
tl:dr 有一个浮点数,希望 JsonNET 将其尽可能精确地序列化为最终的 json 字符串。
附:我在这里阅读了大量的问题和其他地方的博客文章,但没有发现任何人试图解决同样的问题,大多数搜索命中都与浮动阅读问题有关(我稍后也需要解决)。
UPDATE:正如下面的 @dbc 所指出的 - Jsont.NET 尊重“TypeConverter”属性,因此我必须制作自己的转换器来覆盖它。
Json.NET将序列化float
使用往返精度格式的值"R"
(source https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonConvert.cs#L261)。但是,您使用的类型,https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.cs https://github.com/FNA-XNA/FNA/blob/master/src/Vector2.cs, has a TypeConverter
应用:
[Serializable]
[TypeConverter(typeof(Vector2Converter))]
[DebuggerDisplay("{DebugDisplayString,nq}")]
public struct Vector2 : IEquatable<Vector2>
{
//...
正如中所解释的牛顿软件文档 http://www.newtonsoft.com/json/help/html/SerializationGuide.htm,此类类型将使用转换器序列化为字符串:
原始类型
.Net: TypeConverter
(可转换为字符串)
JSON:字符串
并且,检查代码https://github.com/FNA-XNA/FNA/blob/master/src/Design/Vector2Converter.cs https://github.com/FNA-XNA/FNA/blob/master/src/Design/Vector2Converter.cs,看来这个转换器是not转换为字符串时使用往返精度格式,位于60号线左右 https://github.com/FNA-XNA/FNA/blob/master/src/Design/Vector2Converter.cs#L60:
return string.Join(
culture.TextInfo.ListSeparator,
new string[]
{
vec.X.ToString(culture),
vec.Y.ToString(culture)
}
);
因此内置的TypeConverter
它本身就是你失去精度的地方。
为了避免这个问题,您可以
-
创建一个custom JsonConverter http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm for Vector2
例如以下内容:
public class Vector2Converter : JsonConverter
{
class Vector2DTO
{
public float X;
public float Y;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Vector2) || objectType == typeof(Vector2?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// A JSON object is an unordered set of name/value pairs so the converter should handle
// the X and Y properties in any order.
var dto = serializer.Deserialize<Vector2DTO>(reader);
if (dto == null)
return null;
return new Vector2(dto.X, dto.Y);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var vec = (Vector2)value;
serializer.Serialize(writer, new Vector2DTO { X = vec.X, Y = vec.Y });
}
}
如果您将转换器添加到JsonSerializerSettings.Converters https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_Converters.htm它将取代TypeConverter
.
工作样本小提琴here https://dotnetfiddle.net/kDHzkd.
转换器将序列化Vector2
作为一个对象X
and Y
属性,但如果您愿意,也可以将其序列化为具有两个值的数组。我不建议将序列化为原始类型string
since Vector2
不对应于 JSON 原语。
Use a 自定义合约解析器 https://www.newtonsoft.com/json/help/html/contractresolver.htm#CustomIContractResolverExamples这不会为类型创建原始契约TypeConverter
应用,例如答案中所示的Newtonsoft.JSON 无法使用 TypeConverter 属性转换模型 https://stackoverflow.com/q/31325866/3744182.
Notes:
-
往返形式"R"
显然不保留之间的差异+0.0
and -0.0
,所以 Json.NET 也没有,请参阅https://dotnetfiddle.net/aereJ2 https://dotnetfiddle.net/aereJ2 or https://dotnetfiddle.net/DwoGyX https://dotnetfiddle.net/DwoGyX进行演示。这微软文档 https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#the-round-trip-r-format-specifier没有提及使用此格式时是否应保留零符号。
因此,这是一种往返 a 的情况float
可能会导致二进制更改。
(谢谢@chux https://stackoverflow.com/users/2410359/chux提出这个问题comments https://stackoverflow.com/questions/51703481/serializing-float-with-maximum-precision-without-changing-target-type/51705781#comment90378398_51705781.)
-
顺便说一句,Json.NET 也使用"R"
写作时double
(如图所示来源JsonConvert.ToString(double value, ...) https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonConvert.cs#L296):
internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable)
{
return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable);
}
For double
only这种格式被记录为可能会失去精度。从往返(“R”)格式说明符 https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#the-round-trip-r-format-specifier:
在某些情况下,如果使用“R”标准数字格式字符串进行格式化的 Double 值无法成功往返/platform:x64
or /platform:anycpu
交换机并在 64 位系统上运行。
因此往返adouble
通过 Json.NET 可能会导致小的二进制差异。然而,这并不严格适用于这里的问题,该问题具体是关于float
.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)