2017-01-30 87 views
4

反序列化对象时,我有以下测试代码:意外标记在JsonConvert.DeserializeObject

[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.
  • 反序列化对象时的意外标记:布尔值。 Path'Mybool',line 1,position 34.

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

回答

4

更新

报告为Issue 1194: JsonTextReader.ParseNumber leads to error after ThrowReaderError,并在当时的建设是不可再现随后被发布了作为Json.NET 10.0.1通过Newtonsoft关闭。

原来的答案

这可能是一个错误的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 
+1

的workouround适用于我的测试项目。非常感谢。我现在按照你的建议在github上报告了一个问题。 – huha