我有以下测试代码:
[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个错误。其中一个是意料之中的事,但另一个不是。错误为:
- JSON整数3554860000对于Int32来说太大或太小。路径"myint",第1行,位置19
- 反序列化对象时出现意外标记:布尔值。路径"Mybool",第1行,位置34
尽管第一个错误被标记为已处理,但为什么会出现第二个错误。我已经从Newtonsoft.Json 8.0.2更新到9.0.1,但它仍然存在。当传递第一个字符串(json1而不是json2)时,就不会出现任何错误。
更新
报告为问题1194:JsonTextReader.ParseNumber在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
的值尚未更新,并且仍然返回成功解析的最后一个令牌的JsonToken.PropertyName
,即"myint"
名称。后来,在吞下并忽略异常之后,流位置和当前令牌值之间的不一致导致"Mybool"
属性名称被跳过,从而导致第二个错误。
如果在调试器中,当抛出异常时,我手动调用
SetToken(JsonToken.Undefined);
ClearRecentString();
然后可以成功解析文件的其余部分。(我不确定JsonToken.Undefined
是否是正确的选择。)
您可能需要向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