2017-01-30 36 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