在JsonConvert.DeserializeObject

时间:2017-01-30 12:19:50

标签: c# json.net

我有以下测试代码:

[TestClass]
public class TestJsonDeserialize
{
    public class MyClass
    {
        [JsonProperty("myint")]
        public int MyInt { get; set; }
        [JsonProperty("Mybool")]
        public bool Mybool { get; set; }
    }

    [TestMethod]
    public void Test1()
    {
        var errors = new List<string>();
        var json1 = "{\"myint\":1554860000,\"Mybool\":false}";
        var json2 = "{\"myint\":3554860000,\"Mybool\":false}";
        var i = JsonConvert.DeserializeObject<MyClass>(json2, new JsonSerializerSettings
        {
            Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
            {
                Debug.WriteLine(args.ErrorContext.Error.Message);
                errors.Add(args.ErrorContext.Error.Message);
                args.ErrorContext.Handled = true;
            }
        });
        Assert.IsTrue(errors.Count <= 1);
    }
}

对JsonConvert.DeserializeObject的调用会产生2个错误。其中一个是预期的,但另一个不是。 错误是:

  • 对于Int32,JSON整数3554860000太大或太小。路径'myint',第1行,第19位。
  • 反序列化对象时出现意外的标记:Boolean。 Path'Mybool',第1行,第34位。

尽管第一个错误标记为已处理,但为什么会出现第二个错误。 我已经从Newtonsoft.Json 8.0.2更新到9.0.1但它仍然存在。 传递第一个字符串(json1而不是json2)时,根本不会发生任何错误。

1 个答案:

答案 0 :(得分:4)

<强>更新

报告为Issue 1194: JsonTextReader.ParseNumber leads to error after ThrowReaderError并被Newtonsoft关闭为当时版本中不可复制,随后以Json.NET 10.0.1发布。

原始答案

这可能是JsonTextReader中的错误。

JsonTextReader.ParseNumber(ReadType readType, char firstChar, int initialPosition)中,有以下逻辑,有些简化:

else if (readType == ReadType.ReadAsInt32)
{

// Snip

        int value;
        ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value);
        if (parseResult == ParseResult.Success)
        {
            numberValue = value;
        }
        else if (parseResult == ParseResult.Overflow)
        {
            throw ThrowReaderError("JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
        }
        else
        {
            throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
        }
    }

    numberType = JsonToken.Integer;
}

// Snip
// Finally, after successfully parsing the number

ClearRecentString();

// index has already been updated
SetToken(numberType, numberValue, false);

ThrowReadError()抛出异常时,流位置已超过过大的整数。但是,JsonReader.TokenType的值尚未更新,仍会为成功解析的最后一个令牌(即"myint"名称返回JsonToken.PropertyName。稍后,在吞下并忽略异常之后,流位置和当前标记值之间的不一致会导致跳过"Mybool"属性名称,从而导致第二个错误。

如果在调试器中抛出异常,我手动调用

SetToken(JsonToken.Undefined);
ClearRecentString();

然后可以成功解析文件的其余部分。 (我不确定JsonToken.Undefined在这里是正确的选择。)

您可能希望report an issue转到Newtonsoft。

由于JsonReader未传入错误处理程序,因此我找到的唯一解决方法是子类JsonTextReader,如下所示:

public class FixedJsonTextReader : JsonTextReader
{
    public FixedJsonTextReader(TextReader reader) : base(reader) { }

    public override int? ReadAsInt32()
    {
        try
        {
            return base.ReadAsInt32();
        }
        catch (JsonReaderException)
        {
            if (TokenType == JsonToken.PropertyName)
                SetToken(JsonToken.None);
            throw;
        }
    }
}

然后做:

var errors = new List<string>();
var json2 = "{\"myint\":3554860000,\"Mybool\":false}";

using (var reader = new FixedJsonTextReader(new StringReader(json2)))
{
    var settings = new JsonSerializerSettings
    {
        Error = delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
        {
            Debug.WriteLine(args.ErrorContext.Error.Message);
            errors.Add(args.ErrorContext.Error.Message);
            args.ErrorContext.Handled = true;
        }
    };
    var i = JsonSerializer.CreateDefault(settings).Deserialize<MyClass>(reader);
}
Assert.IsTrue(errors.Count <= 1); // Passes