Unable to deserialize type into JToken

0

I am unable to deserialize the Tags property of my JSON:

{
    "posts": [
        {
            "ID": 7096,
            "title": "Backwards Pipe Operator",
            "URL": "https://bizmonger.wordpress.com/2017/10/26/backwards-pipe-operator/",
            "tags": {
                "F#": {
                    "ID": 6012,
                    "name": "F#"
                      }
                    }
        }
     ]  
}

From the JSON above I provided the following types:

type Post = { 
    title: string
    URL:   string
    Tags:  JToken seq // Used to refer to the string instead of the property type
}

type Response = { posts: Post list }

I'm running into an exception when attempting to deserialize the Tags property of a Post value:

 let settings = JsonSerializerSettings()
 settings.MissingMemberHandling <- MissingMemberHandling.Ignore
 let result = JsonConvert.DeserializeObject<Response>(json, settings)

Newtonsoft.Json.JsonSerializationException occurred
HResult=0x80131500 Message=Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[Newtonsoft.Json.Linq.JToken]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'posts[0].tags.F#', line 1, position 1582. Source=
StackTrace: at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) ...

Appendix:

Here's the JSON URL

try let url =        String.Format(ArticlesUrl, user.AccessId)
    let response =   client.GetAsync(url) |> Async.AwaitTask 
                                          |> Async.RunSynchronously
    if response.IsSuccessStatusCode
    then let json =     response.Content.ReadAsStringAsync() |> Async.AwaitTask 
                                                             |> Async.RunSynchronously
         let settings = JsonSerializerSettings()
         settings.MissingMemberHandling <- MissingMemberHandling.Ignore

         let result = JsonConvert.DeserializeObject<Response>(json, settings);
c#
json
f#
json.net
asked on Stack Overflow Nov 1, 2017 by Scott Nimrod • edited Nov 1, 2017 by Scott Nimrod

1 Answer

2

The library tells you very explicitly what's wrong: Cannot deserialize ... JSON object ... into type 'JToken seq' because the type requires a JSON array to deserialize correctly.

Seq is a sequence (i.e. a list, an array, an enumeration) of stuff. It corresponds to JSON array, not to JSON object.

To deserialize a JSON object without knowing property names in advance, use IDictionary:

type Post = { 
    title: string 
    URL: string 
    Tags: IDictionary<string, JToken>
}

Further, if you do know the shape of each individual tag (ID + name), then you can do even better:

type Tag = { ID: string; name: string }

type Post = { 
    title: string 
    URL: string 
    Tags: IDictionary<string, Tag>
}
answered on Stack Overflow Nov 1, 2017 by Fyodor Soikin

User contributions licensed under CC BY-SA 3.0