2017-01-30 2 views
4

Ich habe den folgenden Testcode:unerwartetes Token, wenn das Objekt in JsonConvert.DeserializeObject Deserialisieren

[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); 
    } 
} 

Der Aufruf von JsonConvert.DeserializeObject erzeugt 2 Fehler. Einer von ihnen wird erwartet, aber der andere nicht. Die Fehler sind:

  • JSON Integer 3554860000 ist zu groß oder klein für ein Int32. Pfad 'myint', Zeile 1, Position 19.
  • Unerwartetes Token beim Deserialisieren von Objekt: Boolean. Pfad 'Mybool', Zeile 1, Position 34.
  • Warum gibt es einen zweiten Fehler, obwohl der erste Fehler als behandelt markiert ist. Ich habe bereits von Newtonsoft.Json 8.0.2 auf 9.0.1 aktualisiert, aber es bleibt. Wenn die erste Zeichenfolge (json1 anstelle von json2) übergeben wird, treten überhaupt keine Fehler auf.

    Antwort

    4

    aktualisieren

    als Issue 1194: JsonTextReader.ParseNumber leads to error after ThrowReaderError Berichtet und dann aktuellen Build als nicht reproduzierbar durch Newtonsoft geschlossen, die anschließend als Json.NET 10.0.1 veröffentlicht wurde.

    Original-Antwort

    Dies kann ein Fehler in JsonTextReader sein.

    In JsonTextReader.ParseNumber(ReadType readType, char firstChar, int initialPosition) es die folgenden Logik ist, etwas vereinfacht:

    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); 
    

    An der Stelle, die Ausnahme von ThrowReadError() geworfen wird, hat der Strom vorbei an der Position zu großen ganzen Zahl vorgeschoben worden ist. Der Wert für JsonReader.TokenType wurde jedoch nicht aktualisiert und gibt weiterhin JsonToken.PropertyName für das letzte erfolgreich analysierte Token zurück, nämlich den Namen "myint". Später, nachdem die Ausnahme verschluckt und ignoriert wurde, führt die Inkonsistenz zwischen der Stream-Position und dem aktuellen Tokenwert dazu, dass der Eigenschaftsname "Mybool" übersprungen wird, was zum zweiten Fehler führt.

    Wenn im Debugger, wenn die Ausnahme, die ich

    SetToken(JsonToken.Undefined); 
    ClearRecentString(); 
    

    Dann kann der Rest der Datei manuell ausgelöst wird

    rufen erfolgreich analysiert werden. (Ich bin mir nicht sicher JsonToken.Undefined ist die richtige Wahl hier.)

    Sie möchten vielleicht report an issue zu Newtonsoft.

    Da die JsonReader nicht in die Fehlerbehandlungsroutine übergeben, die nur Abhilfe ich finden konnte, ist JsonTextReader Unterklasse wie folgt:

    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; 
         } 
        } 
    } 
    

    Und dann tun:

    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

    Die workouround arbeitet für mein Testprojekt. Danke vielmals. Ich habe jetzt ein Problem auf GitHub gemeldet, wie du es vorgeschlagen hast. – huha