2016-09-20 130 views
3

我已将我的Newtonsoft.Json版本从“8.0.3”更新为“9.0.1”, 更改后,我开始面对双打之间的一些转换问题和字符串。在Newtonsoft.Json.net中转换双打和字符串之间的问题

下面是一些代码:

public class KeyValue 
    { 
     public string Key { get; set; } 
     public string Value { get; set; }  
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     var json = "{\"Key\": 'test', \"Value\": 210001.0}"; 
     var kv = JsonConvert.DeserializeObject<KeyValue>(json); 
    } 

在Newtonsoft.Json版 “8.0.3” - 之类的产出将是: 关键 - “测试” 价值 - “210001” //没有。 0

在Newtonsoft.Json版“9.0.1”之类的产出将是 关键 - “测试” 价值 - “210001.0”

这种情况仅在值为210001.0 - 为210001.1它w ^不会发生。 现在我明白新版本更好地解决了这个问题,但是我有很多依赖于旧版本解决方案的外部代码。 如何实现旧版本解决方案?

回答

2

肯定会工作的解决方案是将自定义JsonConverter应用到属性,这将使您能够控制值的序列化/反序列化。

第一步是创建一个新的JsonConverter,这在实现中非常简单。我只是调用ToString()的值,该值可以是decimaldouble

public sealed class FloatStringConverter : JsonConverter 
{ 
    public override bool CanWrite { get { return false; } } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotSupportedException(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     return reader.Value.ToString(); 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(double) || objectType == typeof(decimal); 
    } 
} 

我做了它,所以它可以与FloatParseHandling两个值工作,正如我在以前的,不正确的答案提出将格式化值。你当然可以根据自己的喜好调整它,如果你想忽略FloatParseHandling,但它需要进行类型检查。

请记住,FloatParseHandling的默认值是FloatParseHandling.Double,所以它可以在不更改串行器设置的情况下正常工作。

接下来你需要做的是用适当的属性标记属性,以便序列化器知道使用它。

[JsonConverter(typeof(FloatStringConverter))] 
public string Value { get; set; } 

在此之后,Value将包含许多来自JSON正确的表示。

+0

我不想通过转换器实现这一点,但我想这是迄今为止提出的最佳解决方案。非常感谢你!!! –

+0

@olegchernyakov,作为一种预防措施,我建议稍微改变转换器的实现,并用'throw new NotSupportedException();'替换'CanConvert'的主体。由于'CanConvert'在属性被标记为属性时没有被调用,只有当转换器被添加到集合'JsonSerializerSettings.Converters'时,它可能会产生一些问题,因为'CanConvert'会收到一种属性,在你的情况下是'串'而不是数字类型。移除主体将确保此转换器仅与'JsonConverterAttribute'一起使用。 – kiziu

3

发生这种情况,因为Newtonsoft.Json的显然作者更改了默认值JsonSerializerSettings.FloatParseHandling。这改变了数字字符串转换中使用的中间类型。 0出现在字符串的结尾,因为解串器使用Decimal的号码类型,当你尝试运行

210001.0M.ToString() 

210001.0 

要恢复到旧的处理,这是Double数字,你必须在反序列化时明确指定它,例如。

JsonConvert.DeserializeObject<KeyValue>(json, new JsonSerializerSettings { FloatParseHandling = FloatParseHandling.Double }); 

这使得解串器来执行它作为

210001D.ToString() 

返回

210001 

参见demo at dotnetfiddle.net

请注意,从Decimal更改为Double时可能会失去一些精度。

编辑: 我已经将问题追溯到JsonTextReader.ReadStringValue稍微改变的实施。在版本8.0.3中,它使用数字类型作为中间数字,因此在从floatstring的反序列化中使用FloatParseHandling。在版本9.0.1中,它不再使用数字类型,它只是使用JSON的子字符串并将其设置为值。

综上所述,我对JSON.net的建议不是很熟悉,并且从我的其他答案中建议除JsonConverter之外的其他选项。也许更熟悉它的人可以提供其他的东西。

+0

我跳过了一个事实,即依赖于一个数字的代码格式不同,但仍然正确,有点味道。除非它有更深的含义。 – kiziu

+0

感谢您的回答。当这段代码在dotnetfiddle上成功运行时,当我将它复制到一个winform时,它们(double \ decimal)都会用'.0'产生结果。我使用Json.net 9.0.1包,我的项目使用4.5框架。你可以在你的PC上打开一个winform并检查结果吗? –

+1

你是对的,在本地执行时不起作用。此外,似乎整个反序列化的执行方式与我想象的完全不同,这使我的答案不正确 - 我很抱歉。如果destination属性是'string',则不进行任何转换,并且在JSON中传递的值被视为字符串并用作值。我会发布一个解决方案,但它需要更多的研究和调试来追踪你遇到的变化的来源。 – kiziu