Newtonsoft.Json - 反序列化不带引号的大写布尔值

我从 API 收到一些不太符合 ISO 标准的 Json 内容。布尔值为大写而不是小写。

{ "Bool": False }

最初,我认为通过使用自定义应该很容易解决,例如如何让 newtonsoft 将 yes 和 no 反序列化为布尔值JsonConverter

但看起来该方法从未被调用过。我认为原因是,该值不在引号中,因此永远不会调用转换器并创建异常。JsonConverter.ReadJsonFalseJsonTextReader

处理这种情况的最佳方法是什么?

public class BoolTests

{

    public class A

    {

        [JsonConverter(typeof(CaseIgnoringBooleanConverter))]

        public bool Bool { get; set; }

    }



    [Theory]

    [InlineData(false, "{'Bool': false}")] //ok

    [InlineData(false, "{'Bool': 'False'}")] // ok

    [InlineData(false, "{'Bool': False")] // fails

    public void CasingMatters(bool expected, string json)

    {

        var actual = JsonConvert.DeserializeObject<A>(json);

        Assert.Equal(expected, actual.Bool);

    }

}


// taken from https://gist.github.com/randyburden/5924981

public class CaseIgnoringBooleanConverter : JsonConverter

{

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

    {

        switch (reader.Value.ToString().ToUpperInvariant().Trim())

        {

            case "TRUE":

                return true;

            case "FALSE":

                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 bool CanConvert(Type objectType)

    {

        return objectType == typeof(bool);

    }


    public override bool CanWrite => false;


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

    {

        throw new NotImplementedException();

    }

}


开心每一天1111
浏览 173回答 2
2回答

30秒到达战场

不幸的是,正如您所发现的,无效的 json 是无效的,因此无法由普通和常见的 json (de) 序列化程序(如 Json.net)处理。对反序列化程序使用转换器和策略设置也不起作用,因为它们旨在处理诸如空对象作为数组返回或名称转换/大小写处理之类的事情。一个天真的解决方案是做一个简单的字符串替换,比如string json = invalidJson.Replace("False", "false");但是,这存在一些问题:您需要将整个无效的 json 读入内存,并创建它的固定副本,这意味着您将在内存中拥有两个完整的数据副本,一个是坏的,一个是好的。它也会替换内部字符串。这可能不是您的数据问题,但使用上述方法并不容易处理。False另一种方法是编写一个基本的分词器,该分词器可以理解基本的 JSON 语法,例如字符串、数字和标识符,并逐个令牌遍历文件,替换错误的标识符。这将解决问题 2,但根据解决方案,可能需要更复杂的实现来修复内存问题 1。下面发布了一个简单的尝试,创建一个可以使用的标识符,这将在找到标识符时修复它们,否则可以理解基本的 JSON 令牌。TextReader请注意以下几点:它不是真正的性能。它始终分配临时缓冲区。您可能希望研究“缓冲区租用”以更好地处理此方法,甚至只是直接流式传输到缓冲区。它不处理数字,因为那时我停止编写代码。我把这个留作练习。可以编写基本的数字处理,因为您并没有真正验证文件是否具有有效的 JSON,因此可以添加任何可以获取足够字符来构成数字的内容。我没有用非常大的文件对此进行测试,只用小的示例文件进行测试。我复制了一个9.5MB的文本,它适用于此。List<Test>我没有测试所有 JSON 语法。可能存在应该处理但未处理的字符。如果您最终使用它,请创建大量测试!但是,它的作用是根据您发布的标识符修复无效的 JSON,并且它以流式方式执行此操作。因此,无论您的 JSON 文件有多大,这都应该可用。无论如何,这是代码,再次注意有关数字的异常:void Main(){    using (var file = File.OpenText(@"d:\temp\test.json"))    using (var fix = new MyFalseFixingTextReader(file))    {        var reader = new JsonTextReader(fix);        var serializer = new JsonSerializer();        serializer.Deserialize<Test>(reader).Dump();    }}public class MyFalseFixingTextReader : TextReader{    private readonly TextReader _Reader;    private readonly StringBuilder _Buffer = new StringBuilder(32768);    public MyFalseFixingTextReader(TextReader reader) => _Reader = reader;    public override void Close()    {        _Reader.Close();        base.Close();    }    public override int Read(char[] buffer, int index, int count)    {        TryFillBuffer(count);        int amountToCopy = Math.Min(_Buffer.Length, count);        _Buffer.CopyTo(0, buffer, index, amountToCopy);        _Buffer.Remove(0, amountToCopy);        return amountToCopy;    }    private (bool more, char c) TryReadChar()    {        int i = _Reader.Read();        if (i < 0)            return (false, default);        return (true, (char)i);    }    private (bool more, char c) TryPeekChar()    {        int i = _Reader.Peek();        if (i < 0)            return (false, default);        return (true, (char)i);    }    private void TryFillBuffer(int count)    {        if (_Buffer.Length >= count)            return;        while (_Buffer.Length < count)        {            var (more, c) = TryPeekChar();            if (!more)                break;            switch (c)            {                case '{':                case '}':                case '[':                case ']':                case '\r':                case '\n':                case ' ':                case '\t':                case ':':                case ',':                    _Reader.Read();                    _Buffer.Append(c);                    break;                case '"':                    _Buffer.Append(GrabString());                    break;                case char letter when char.IsLetter(letter):                    var identifier = GrabIdentifier();                    _Buffer.Append(ReplaceFaultyIdentifiers(identifier));                    break;                case char startOfNumber when startOfNumber == '-' || (startOfNumber >= '0' && startOfNumber <= '9'):                    _Buffer.Append(GrabNumber());                    break;                default:                    throw new InvalidOperationException($"Unable to cope with character '{c}' (0x{((int)c).ToString("x2")})");            }        }    }    private string ReplaceFaultyIdentifiers(string identifier)    {        switch (identifier)        {            case "False":                return "false";            case "True":                return "true";            case "Null":                return "null";            default:                return identifier;        }    }    private string GrabNumber()    {        throw new NotImplementedException("Left as an excercise");        // See https://www.json.org/ for the syntax    }    private string GrabIdentifier()    {        var result = new StringBuilder();        while (true)        {            int i = _Reader.Peek();            if (i < 0)                break;            char c = (char)i;            if (char.IsLetter(c))            {                _Reader.Read();                result.Append(c);            }            else                break;        }        return result.ToString();    }    private string GrabString()    {        _Reader.Read();        var result = new StringBuilder();        result.Append('"');        while (true)        {            var (more, c) = TryReadChar();            if (!more)                return result.ToString();            switch (c)            {                case '"':                    result.Append(c);                    return result.ToString();                case '\\':                    result.Append(c);                    (more, c) = TryReadChar();                    if (!more)                        return result.ToString();                    switch (c)                    {                        case 'u':                            result.Append(c);                            for (int index = 1; index <= 4; index++)                            {                                (more, c) = TryReadChar();                                if (!more)                                    return result.ToString();                                result.Append(c);                            }                            break;                        default:                            result.Append(c);                            break;                    }                    break;                default:                    result.Append(c);                    break;            }        }    }}public class Test{    public bool False1 { get; set; }    public bool False2 { get; set; }    public bool False3 { get; set; }}示例文件:{    "false1": false,    "false2": "false",    "false3": False}输出:

蓝山帝景

应在源中修复无效的 json。如果你真的需要按原样解析它,如果你想把它作为一个字符串,你可以用“False”替换 False,如果你想把它作为一个布尔值,你可以用 false 替换它。// If you want a stringjson.Replace("False", "\"False\"");// If you want a booljson.Replace("False", "false");一个问题是,如果一个键或其他值包含“False”模式。
打开App,查看更多内容
随时随地看视频慕课网APP