2015-04-18 12 views
7

Ich habe schon seit einiger Zeit versucht, einen JSON Deserialiser zu schreiben, konnte aber meinen Fehler nicht finden. Warum sagt mir Newtonsoft, Unexpected token when deserializing object: StartObject, nach der Deserialisierung?Newtonsoft Converter FromJson - unerwartetes Token

type ThisFails = 
    { a : string * string 
    m : Map<string, string> } 

type ThisWorks = 
    { y : Map<string, string> 
    z : string * string } 

testCase "failing test - array before object" <| fun _ -> 
    let res = deserialise<ThisFails> Serialisation.converters 
            """{"a":["xyz","zyx"],"m":{}}""" 
    Assert.Equal("should be eq to res", { a = "xyz", "zyx"; m = Map.empty }, res) 

testCase "passing test - array after object" <| fun _ -> 
    let res = deserialise<ThisWorks> Serialisation.converters 
            """{"y":{},"z":["xyz","zyx"]}""" 
    Assert.Equal("should be eq to res", { y = Map.empty; z = "xyz", "zyx" }, res) 

Der Betreff is the TupleArrayConverter.

Die Spur dieses Konverter ist:

reading json [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    type => System.Tuple`2[System.String,System.String] 

value token, pre-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    path => "a[0]" 
    token_type => String 

value token, post-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    path => "a[1]" 
    token_type => String 

value token, pre-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    path => "a[1]" 
    token_type => String 

value token, post-deserialise [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    path => "a" 
    token_type => EndArray 

after EndArray token, returning [Newtonsoft.Json.FSharp.TupleArrayConverter] 
    path => "m" 
    token_type => PropertyName 

Im Konverter, ich bin raubend das letzte Zeichen, das Ende Array, wie Sie im Abschluss Fall sehen:

match reader.TokenType with 
| JsonToken.EndArray -> 
    read JsonToken.EndArray |> req |> ignore 
    acc 

Und ich verzehre das StartArray Token am Anfang ...

Also: warum funktioniert dieser Code nicht? (Newtonsoft.Json 6.0.8)

Dies ist der Fehler:

map tests/failing test - array before object: Exception: Newtonsoft.Json.JsonSerializationException: Unexpected token when deserializing object: StartObject. Path 'm', line 1, position 24. 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues (Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.JsonReader reader, System.Type objectType, IDictionary`2& extensionData) [0x00000] in <filename unknown>:0 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.Serialization.ObjectConstructor`1 creator, System.String id) [0x00000] in <filename unknown>:0 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <filename unknown>:0 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00000] in <filename unknown>:0 
    at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, Boolean checkAdditionalContent) [0x00000] in <filename unknown>:0 (00:00:00.1500996) 
1 tests run: 0 passed, 0 ignored, 0 failed, 1 errored (00:00:00.2482623) 

Antwort

1

worden Debuggen von Code zusammen mit JSON.Net.

Es stellt sich heraus, dass nach dem Verbrauch der EndArray Token in Ihrem Fall schlägt der Leser dann auf ein PropertyName Token, das ist alles gut.

Dann, nachdem der Konverter die Ausführung abgeschlossen hat, führt JSON.Net das aus.

} while (!exit && reader.Read()); 

Read() bewegt dann den Leser auf das nächste Token, die in ihrem Versagen Fall ist StartObject verursacht der Deserializer zum Scheitern verurteilt.

Also, ich bin kein Experte in JSON.Net, aber darüber nachdenken, einen Anbieter für einen String-Wert in JSON.Net zur Verfügung zu stellen Ich würde wahrscheinlich nicht den Leser nach der Konvertierung voranbringen Bedeutung der Leser zeigt immer noch auf den String-Wert. Entlang der gleichen Denkrichtung macht es Sinn, wenn ein Array konsumiert wird, um das Lesegerät zum letzten Token des Array-Werts zu lassen, dh das Token EndArray.

So ist mein Vorschlag nur das:

match reader.TokenType with 
| JsonToken.EndArray -> 
// read JsonToken.EndArray |> req |> ignore 

    Logger.debug logger <| fun _ -> 
    LogLine.sprintf 
     [ "path", reader.Path |> box 
     "token_type", reader.TokenType |> box ] 
     "after EndArray token, returning" 

    acc 

Das ist mein Testprogramm macht:

[<EntryPoint>] 
let main argv = 
    let works = deserialize<ThisWorks> """{"y":{},"z":["xyz","zyx"]}""" 
    printfn "%A" works 

    let fails = deserialize<ThisFails> """{"a":["xyz","zyx"],"m":{}}""" 
    printfn "%A" fails 

    0 

Drucken

{y = map []; 
z = ("xyz", "zyx");} 
{a = ("xyz", "zyx"); 
m = map [];} 

Hoffentlich hilft Ihnen, diesen Fehler zu beheben (Sie vielleicht schon getan haben)

+1

Lass es mich überprüfen! – Henrik

+0

Hoffentlich lief ich gegen die richtige Version (Git-Tag 6.0.8) – FuleSnabel