如何让newtonsoft将yes和no反序列化为布尔值

Posted

技术标签:

【中文标题】如何让newtonsoft将yes和no反序列化为布尔值【英文标题】:how to get newtonsoft to deserialize yes and no to boolean 【发布时间】:2013-01-09 14:03:40 【问题描述】:

注意:我已在此提要底部提供了解决方案。

我有一个 C# Win 8 应用程序,我正在反序列化一些看起来像这样的 json:


    'Unit': [
        
            'name':'House 123',
            isAvailable:'no'
        ,
        
            'name':'House 456',
            isAvailable:'yes'
        ]

进入使用此接口的类:

public interface IUnit

    string Name  get; 
    bool isAvailable  get; 

但是 Newtonsoft 抛出错误:

解析值时遇到意外字符:n。小路 'Unit[0].isAvailable,第 1 行,第 42 位。

有没有办法扩展 Newtonsoft 以根据结果对象属性类型 bool 解析 yes/no 或 1/0?现在它只适用于真/假。

有几篇关于类的自定义转换器的帖子,但不是像 bool 这样的原始类型。

有什么建议吗?

【问题讨论】:

【参考方案1】:

这是@John 在 vb 中的解决方案的一个版本,供任何需要的人使用。它处理布尔值和可为空的布尔值。在写入时它转换为 0/1 以节省传输中的一些字节(而不是真/假):

Imports Newtonsoft.Json

Public Class MyBooleanConverter
    Inherits JsonConverter

Public Overrides ReadOnly Property CanWrite As Boolean
    Get
        Return True
    End Get
End Property

Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
    Dim boolVal As Boolean = value
    writer.WriteValue(If(boolVal, 1, 0))
End Sub

Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
    Dim value = reader.Value
    If IsNothing(value) OrElse String.IsNullOrWhiteSpace(value.ToString()) OrElse "0" = value Then
        Return False
    End If
    If 0 = String.Compare("yes", value, True) OrElse 0 = String.Compare("true", value, True) Then
        Return True
    End If
    Return False
End Function

Public Overrides Function CanConvert(objectType As Type) As Boolean
    Return objectType = GetType(Boolean) OrElse objectType = GetType(Boolean?) 'OrElse objectType = GetType(String)
End Function
End Class

【讨论】:

【参考方案2】:

这是我想出来的。

public class JsonBooleanConverter : JsonConverter

    public override bool CanWrite  get  return false;  

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        var value = reader.Value.ToString().ToLower().Trim();
        switch (value)
        
            case "true":
            case "yes":
            case "y":
            case "1":
                return true;
        
        return false;
    

    public override bool CanConvert(Type objectType)
    
        if (objectType == typeof(Boolean))
        
            return true;
        
        return false;
    

用法:

var myObj = JsonConvert.DeserializeObject<T>(json, new JsonBooleanConverter());

【讨论】:

这确实有效。 github.com/petekapakos/JsonBooleanConverterTest 是的,它确实适用于您精心设计的特定测试用例。试试这个:用两个调用替换 main() 函数中的所有内容:JsonConvert.DeserializeObject&lt;bool&gt;("true", new JsonBooleanConverter()); JsonConvert.DeserializeObject&lt;bool&gt;("yes", new JsonBooleanConverter()); 第一个会成功,后者会失败,并出现以下异常:"Unexpected character encountered while parsing value: y. Path '', line 0, position 0."。这是因为您误用了 Converter:它不是格式错误 JSON 的预解析修复机制,这就是它在此处使用的方式。【参考方案3】:
public class MyBooleanConverter : JsonConverter

    public override bool CanWrite  get  return false;  

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        var value = reader.Value;

        if (value == null || String.IsNullOrWhiteSpace(value.ToString()))
        
            return false;
        

        if ("yes".Equals(value, StringComparison.OrdinalIgnoreCase))
        
            return true;
        

        return false;
    

    public override bool CanConvert(Type objectType)
    
        if (objectType == typeof(String) || objectType == typeof(Boolean))
        
            return true;
        
        return false;
    



public interface IUnit

    string Name  get; 

    [JsonConverter(typeof(MyBooleanConverter))]
    bool isAvailable  get; 

【讨论】:

感谢克雷格的快速响应。这看起来比我想出的解决方案更好,但我在让它与我的代码一起工作时遇到问题:我已将其发布在下一个答案中...... Newtonsoft.Json.JsonSerializer 序列化器 = new Newtonsoft.Json.JsonSerializer(); serializer.Converters.Add(new MyBooleanConverter()) string json = "'Unit':['name':'Apartment 123',isSingleUnit:'no','name':'House 456',isSingleUnit: 'yes']".Replace('\'', '\"'); var obj = serializer.Deserialize(new StringReader(json), typeof(bool)); Console.WriteLine(obj); 它只是返回“假”。 基础知识+1;不过,您的代码可能会被严重削减:) 考虑这个:***.com/a/809558/820068 然后考虑将比较改为:"if ("yes".Equals(value.ToString() ..."【参考方案4】:

我建议this approach

using System;
using Newtonsoft.Json;

namespace JsonConverters

    public class BooleanJsonConverter : JsonConverter
    
        public override bool CanConvert( Type objectType )
        
            return objectType == typeof( bool );
        

        public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
        
            switch ( reader.Value.ToString().ToLower().Trim() )
            
                case "true":
                case "yes":
                case "y":
                case "1":
                    return true;
                case "false":
                case "no":
                case "n":
                case "0":
                    return false;
            

            // If we reach here, we're pretty much going to throw an error so let's let Json.NET throw it's pretty-fied error message.
            return new JsonSerializer().Deserialize( reader, objectType );
        

        public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
        
        

    

【讨论】:

【参考方案5】:

//这是我想出来的……

   using System;
 using System.Collections.Generic;
 using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace NewtonTest


internal class NewtonTest

    public class Data
    
        public IEnumerable<IUnit> Unit  get; set; 

        public override string ToString()
        
            return string.Format("DataUnit=[0]",
                string.Join(", ", Unit.Select(c =>
                                string.Format("0 - Single Unit: 1", 
                                    c.Name,
                                    c.isSingleUnit.ToString()))));
        
    

    public interface IUnit
    
        string Name  get; 

        // [JsonConverter(typeof(Converter))]
        bool isSingleUnit  get; 
    

    public class House : IUnit
    
        public House(string name, bool isSingle)
        
            this.Name = name;
            this.isSingleUnit = isSingle;
        

        public string Name  get; private set; 

        public bool isSingleUnit  get; private set; 
    

    public class Apartment : IUnit
    
        public Apartment(string name, bool isSingle)
        
            this.Name = name;
            this.isSingleUnit = isSingle;
        

        public string Name  get; private set; 

        public bool isSingleUnit  get; private set; 
    

    private static bool ConvertToBool(string value)
    
        value =
            value.ToUpper().
                  Replace("YES", "TRUE").
                  Replace("Y", "TRUE").
                  Replace("1", "TRUE").
                  Replace("NO", "FALSE").
                  Replace("N", "FALSE").
                  Replace("0", "FALSE");

        bool result = false;

        bool.TryParse(value, out result);

        return result;
    


    private class UnitConverter : Newtonsoft.Json.JsonConverter
    
        public override bool CanConvert(Type objectType)
        
            return typeof (NewtonTest.IUnit).IsAssignableFrom(objectType);
        

        public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue,
                                        Newtonsoft.Json.JsonSerializer serializer)
        
            JObject obj = serializer.Deserialize<JToken>(reader) as JObject;

            if (obj != null)
            
                string result = obj["isSingleUnit"].ToObject<string>();

                bool isSingleUnit = ConvertToBool(result);

                string name = obj["name"].ToObject<string>();

                if (isSingleUnit)
                
                    return new NewtonTest.House(name, isSingleUnit);
                
                else
                
                    return new NewtonTest.Apartment(name, isSingleUnit);
                
            
            else
            
                return null;
            
        

        public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value,
                                       Newtonsoft.Json.JsonSerializer serializer)
        
            throw new NotImplementedException();
        
    


    public static void Main()
    
        Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
        serializer.Converters.Add(new UnitConverter());
        string json =
            "'Unit':['name':'Apartment 123',isSingleUnit:'no','name':'House 456',isSingleUnit:'yes']".Replace(
                '\'', '\"');
        var obj = serializer.Deserialize(new StringReader(json), typeof (Data));
        Console.WriteLine(obj);
        Console.ReadKey();
    


【讨论】:

我看不出你在哪里告诉反序列化器你期望IUnit。它不会知道应用正确的转换器。 抱歉,代码嵌套的方式很难阅读。它在这个方法中: public override bool CanConvert(Type objectType) return typeof (NewtonTest.IUnit).IsAssignableFrom(objectType); 不够。您需要在 Main() 中引用 IUnit

以上是关于如何让newtonsoft将yes和no反序列化为布尔值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NewtonSoft Json.Net 将 Json 字典反序列化为平面类

Newtonsoft Json 将字典反序列化为来自 DataContractJsonSerializer 的键/值列表

csharp 使用Newtonsoft JSON.NET将任何对象序列化/反序列化为JSON

使用 Newtonsoft 将 JSON 反序列化为 .NET 对象(或者可能是 LINQ to JSON?)

Newtonsoft 转义 JSON 字符串无法反序列化为对象

将JSON反序列化为相对类