8oris
8oris

Reputation: 453

.NET Nested Arrays In JSON

Here's my json:

{
  "total": 1,
  "page": 1,
  "per_page": 25,
  "paging": {
    "next": null,
    "previous": null,
    "first": "/users/4061973/pictures?access_token=&page=1",
    "last": "/users/4061973/pictures?access_token=&page=1"
  },
  "data": [
    {
      "uri": "/users/4061973/pictures/32598345",
      "active": true,
      "type": "custom",
      "sizes": [
        {
          "width": 30,
          "height": 30,
          "link": "https://i.vimeocdn.com/portrait/32598345_30x30"
        },
        {
          "width": 72,
          "height": 72,
          "link": "https://i.vimeocdn.com/portrait/32598345_72x72"
        },
        {
          "width": 75,
          "height": 75,
          "link": "https://i.vimeocdn.com/portrait/32598345_75x75"
        },
        {
          "width": 100,
          "height": 100,
          "link": "https://i.vimeocdn.com/portrait/32598345_100x100"
        },
        {
          "width": 144,
          "height": 144,
          "link": "https://i.vimeocdn.com/portrait/32598345_144x144"
        },
        {
          "width": 216,
          "height": 216,
          "link": "https://i.vimeocdn.com/portrait/32598345_216x216"
        },
        {
          "width": 288,
          "height": 288,
          "link": "https://i.vimeocdn.com/portrait/32598345_288x288"
        },
        {
          "width": 300,
          "height": 300,
          "link": "https://i.vimeocdn.com/portrait/32598345_300x300"
        },
        {
          "width": 360,
          "height": 360,
          "link": "https://i.vimeocdn.com/portrait/32598345_360x360"
        }
      ],
      "resource_key": "",
      "default_picture": false
    }
  ]
}

I want to get the links of all images. I have tried:

vimeo_pictures_api_response_json = Newtonsoft.Json.Linq.JObject.Parse(vimeo_pictures_response_request_txt.ToString())
Dim vimeo_pictures_list As String = vimeo_pictures_api_response_json("data")(0)("sizes")

But, I have this error: Can not convert array to string
So, I tried this:

vimeo_pictures_api_response_json = Newtonsoft.Json.Linq.JObject.Parse(vimeo_pictures_response_request_txt.ToString())
Dim vimeo_pictures_list As String = vimeo_pictures_api_response_json("data")(0)("sizes")

But, I have the same error. I also tried:

vimeo_pictures_api_response_json = Newtonsoft.Json.Linq.JObject.Parse(vimeo_pictures_response_request_txt.ToString())
Dim vimeo_pictures_list As Array= vimeo_pictures_api_response_json("data")(0)("sizes")

But, I have a new error: Can not convert array to byte array

How should I proceed?

Upvotes: 1

Views: 272

Answers (2)

Brian Rogers
Brian Rogers

Reputation: 129707

You could use SelectTokens with the JsonPath recursive descent operator .. to get all the links in one go:

Dim links = JObject.Parse(json).SelectTokens("..link").Select(Function(t) CStr(t)).ToList()

Demo fiddle: https://dotnetfiddle.net/QNJTek

Upvotes: 0

Jimi
Jimi

Reputation: 32248

In your JSON, sizes is an array of objects. It's no clear what value this was supposed to retrieve:

Dim vimeo_pictures_list As String = vimeo_pictures_api_response_json("data")(0)("sizes")

If what you want to do is to get the first link (the only string Type there), then you'd write:

Dim vimeoObjects = JObject.Parse([API Response])
Dim jArraySizes = vimeoObjects("data").First()("sizes")
Dim firstLink = vimeoObjects("data").First()("sizes").First()("link")
' Or
Dim firstLink = vimeoObjects("data")(0)("sizes")(0)("link")

Note that jArraySizes, here, is inferred as a JToken: of course, at this time, what sizes will be is unknown, so the Type is always JToken, the base class. We know it will be an array of JObjects, thus a JArray here, because we can determine this by looking at the JSON structure.

Hence, this doesn't make much sense:

Dim vimeo_pictures_list As Array= vimeo_pictures_api_response_json("data")(0)("sizes")

You probably have set Option Strict Off, otherwise it would be impossible to compile the code, since you're specifying the wrong conversion Type (as before).

Hence, to get, e.g., all link strings in the array, we can write:

For Each jSize In jArraySizes
    Console.WriteLine(jSize("link"))
Next

Here, jSize is of course still inferred as a JToken, for the same reason. We know it will be a JObject (for the same reason: we have eyes).

If you want to avoid to always go back to the JSON structure and determine what kind of Type you'll be dealing with before doing anything with it, you can pre-build a Class Model that describes the JSON and deserialize to this model instead.
▶ When you do this, Intellisense will then kick in and it will show you what Type/Value each of the Properties is/has.

You can use on-line resources, as JSON Utils (since it can generate VB.Net code), to convert your JSON to a .Net class structure.
Or, if the JSON is (very) simple, copy the JSON, then open the Edit menu of Visual Studio and select
Paste Special -> Paste JSON as Classes.

Using a class Model, we can also add methods that simplify the deserialization / serialization of the JSON. Here, I've added A Public Shared Function Deserialize() method. It's static (Shared), so you can call it directly:

Dim vimeoPictures = VimeoPicturesHandler.Deserialize([API Response])

Note that some Properties have a <JsonProperty()> descriptor Attribute, since some Property names in the JSON may conflict with language keywords (as next).

Now you can access each object as a .Net Property value as usual. E.g.,:

Console.WriteLine(vimeoPictures.Paging.FirstPage)
Console.WriteLine(vimeoPictures.Paging.LastPage)

For Each sizeObj In vimeoPictures.Data.First().Sizes
    Console.WriteLine(sizeObj.Link)
    Console.WriteLine(sizeObj.Width)
    Console.WriteLine(sizeObj.Height)
Next

Imports Newtonsoft.Json

Public Class VimeoPicturesHandler

    Public Shared Function Deserialize(json As String) As VimeoPicturesRoot
        Return JsonConvert.DeserializeObject(Of VimeoPicturesRoot)(json)
    End Function

    Public Class VimeoPicturesRoot
        Public Property Total As Integer
        Public Property Page As Integer
        <JsonProperty("per_page")>
        Public Property PerPage As Integer
        Public Property Paging As Paging
        Public Property Data As DataElement()
    End Class

    Public Class Paging
        <JsonProperty("next")>
        Public Property NextPage As String
        <JsonProperty("previous")>
        Public Property PreviousPage As String
        <JsonProperty("first")>
        Public Property FirstPage As String
        <JsonProperty("last")>
        Public Property LastPage As String
    End Class

    Public Class DataElement
        <JsonProperty("uri")>
        Public Property Url As String
        Public Property Active As Boolean
        <JsonProperty("type")>
        Public Property DataType As String
        Public Property Sizes As Size()
        Public Property ResourceKey As String
        <JsonProperty("default_picture")>
        Public Property DefaultPicture As Boolean
    End Class

    Public Class Size
        Public Property Width As Integer
        Public Property Height As Integer
        Public Property Link As String
    End Class
End Class

Upvotes: 2

Related Questions