2

I'm using the Newtonsoft JSON library. I want to loop through a JSON result set without having to create a separate class if possible, since the JSON object is far more extended that displayed here.

I already looked here and here.

My JSON (beautified at bottom of post):

Dim json As String = "{""result"":{""a326f402f18ab1cd2c4489b07cc3e8f4"":{""id"":""a326f402f18ab1cd2c4489b07cc3e8f4"",""client_id"":30,""broker"":[{""broker_id"": 30,""name"": ""Andrew"",""emailaddress"": ""andrew@homes.com""}],""photos"":[{""small"":""https://www.example.com/30/photos/small/66.1427790195-976.jpg"",""middle"":""https://www.example.com/30/photos/middle/66.1427790195-976.jpg""},{""small"":""https://www.example.com/30/photos/small/31382.1508417843-454.JPG"",""middle"":""https://www.example.com/30/photos/middle/31382.1508417843-454.JPG""}]},""18aec266ec0c01d126e9715bc17124e2"":{""id"":""18aec266ec0c01d126e9715bc17124e2"",""client_id"":30,""broker"":[{""broker_id"": 30,""name"": ""Andrew"",""emailaddress"": ""andrew@homes.com""}],""photos"":[{""small"":""https://www.example.com/30/photos/small/10.1298385655.jpg"",""middle"":""https://www.example.com/30/photos/middle/10.1298385655.jpg""},{""small"":""https://www.example.com/30/photos/small/10.1298385646.jpg"",""middle"":""https://www.example.com/30/photos/middle/10.1298385646.jpg""}]}}}"

I first tried with JsonTextReader, but that seemed too cumbersome in trying to access the individual properties quickly:

Dim sBuilder As New StringBuilder
Dim reader As JsonTextReader = New JsonTextReader(New StringReader(json))
While reader.Read
    If reader.Value IsNot Nothing Then
        sBuilder.Append(String.Format("Token: {0}, Value: {1}", reader.TokenType.ToString, reader.Value.ToString))
    Else
        sBuilder.Append(String.Format("Token: {0}", reader.TokenType.ToString))
    End If
    sBuilder.Append("<br/>")
End While

I then tried to work with JObject and JArray. The problem there is that the JSON response is generated by a 3rd party and IMO is not formatted well, since the result object should actually be an array. Also the results contain dynamic IDs (a326f402f18ab1cd2c4489b07cc3e8f4 and 18aec266ec0c01d126e9715bc17124e2)

So, I'm now faced with: how can I loop through all results when result is not an array and each result is also identified by a dynamic id?

Pseudo code:

  1. Loop through the number of results (in this case 2: a326f402f18ab1cd2c4489b07cc3e8f4 and 18aec266ec0c01d126e9715bc17124e2)
  2. For each of those results I want to select the attributes. This does not necessarily have to be strongly typed (json.photos(j).small), I'd be fine with something like json(i)("photos")(j)("small")

_

Dim photoSmall As String
Dim clientId As Integer
For i As Integer = 0 To json.count - 1    
    With json(i)
        clientId = json.client_id           
        For J As Integer= 0 To json.photos.count - 1
            photoSmall = json.photos(j).small       
        Next J      
    End With
Next i

Beautified JSON

{
    "result": {
        "a326f402f18ab1cd2c4489b07cc3e8f4": {
            "id": "a326f402f18ab1cd2c4489b07cc3e8f4",
            "client_id": 30,
            "broker": [
                {
                    "broker_id": 30,
                    "name": "Andrew",
                    "emailaddress": "andrew@homes.com"
                }
            ],
            "photos": [
                {
                    "small": "https://www.example.com/30/photos/small/66.1427790195-976.jpg",
                    "middle": "https://www.example.com/30/photos/middle/66.1427790195-976.jpg"
                },
                {
                    "small": "https://www.example.com/30/photos/small/31382.1508417843-454.JPG",
                    "middle": "https://www.example.com/30/photos/middle/31382.1508417843-454.JPG"
                }
            ]
        },
        "18aec266ec0c01d126e9715bc17124e2": {
            "id": "18aec266ec0c01d126e9715bc17124e2",
            "client_id": 30,
            "broker": [
                {
                    "broker_id": 30,
                    "name": "Andrew",
                    "emailaddress": "andrew@homes.com"
                }
            ],
            "photos": [
                {
                    "small": "https://www.example.com/30/photos/small/10.1298385655.jpg",
                    "middle": "https://www.example.com/30/photos/middle/10.1298385655.jpg"
                },
                {
                    "small": "https://www.example.com/30/photos/small/10.1298385646.jpg",
                    "middle": "https://www.example.com/30/photos/middle/10.1298385646.jpg"
                }
            ]
        }
    }
}

UPDATE 2

This code returns an array

Dim photosTEST As JArray = DirectCast(item("photos"), JArray)
Log("photosTEST length", photosTEST.Count.ToString)

But this code throws error: Object reference not set to an instance of an object

Dim brokers As JArray = DirectCast(item("broker"), JArray)
Log("brokers length", brokers.Count.ToString)

I don't understand since broker is just an array with length of 1 correct?

Adam
  • 6,041
  • 36
  • 120
  • 208

1 Answers1

2

You can cast the result JToken to a JObject and loop through its Properties() collection. The Value of each of those properties is another JObject containing the data (e.g. id, client_id, photos, etc.) you are interested in.

Here is an example:

Dim obj As JObject = JObject.Parse(json)
Dim result As JObject = DirectCast(obj("result"), JObject)
For Each prop As JProperty In result.Properties()

    Dim item As JObject = DirectCast(prop.Value, JObject)
    Dim id As String = item("id").Value(Of String)
    Dim clientId As Integer = item("client_id").Value(Of Integer)
    Console.WriteLine("id: " & id)
    Console.WriteLine("client id: " & clientId.ToString())

    Dim brokers As JArray = DirectCast(item("broker"), JArray)
    For i As Integer = 0 To brokers.Count - 1
        Dim broker As JObject = DirectCast(brokers(i), JObject)
        Dim brokerId As Integer = broker("broker_id").Value(Of Integer)
        Dim name As String = broker("name").Value(Of String)
        Dim email As String = broker("emailaddress").Value(Of String)
        Console.WriteLine("broker " & i.ToString() & " id: " & brokerId)
        Console.WriteLine("broker " & i.ToString() & " name: " & name)
        Console.WriteLine("broker " & i.ToString() & " email: " & email)
    Next

    Dim photos As JArray = DirectCast(item("photos"), JArray)
    For i As Integer = 0 To photos.Count - 1
        Dim photo As JObject = DirectCast(photos(i), JObject)
        Dim small As String = photo("small").Value(Of String)
        Dim middle As String = photo("middle").Value(Of String)
        Console.WriteLine("photo " & i.ToString() & " small: " & small)
        Console.WriteLine("photo " & i.ToString() & " middle: " & middle)
    Next

    Console.WriteLine()
Next

Fiddle: https://dotnetfiddle.net/ALeiX8

Note that the code above assumes that all of the object properties in your example JSON will always be present. If it is possible that a particular property might not appear, then you will need to do a check for Nothing on that property before trying to use its value. For example, you mentioned that you are getting an Object reference not set to an instance of an object error when you try to access the broker count. That tells me that for some of your result items, there is not a broker property in the JSON. In that case, you would need to change the code to check for Nothing like this:

    Dim brokers As JArray = DirectCast(item("broker"), JArray)
    If brokers IsNot Nothing Then
        For i As Integer = 0 To brokers.Count - 1
            Dim broker As JObject = DirectCast(brokers(i), JObject)
            Dim brokerId As Integer = broker("broker_id").Value(Of Integer)
            Dim name As String = broker("name").Value(Of String)
            Dim email As String = broker("emailaddress").Value(Of String)
            Console.WriteLine("broker " & i.ToString() & " id: " & brokerId)
            Console.WriteLine("broker " & i.ToString() & " name: " & name)
            Console.WriteLine("broker " & i.ToString() & " email: " & email)
        Next
    End If

Similarly, if a broker might not have an email address, then you would need to do something like this:

            Dim email As String = ""
            If broker("emailaddress") IsNot Nothing Then
                email = broker("emailaddress").Value(Of String)
            End If

In fact, if you find that there are many properties in the JSON which you cannot count on always being there, you can write a little extension method to help simplify your code. This method will allow you to supply a default value to be used in place of a particular JToken if it turns out to be Nothing:

Imports System.Runtime.CompilerServices
Imports Newtonsoft.Json.Linq

Module JsonExtensions

    <Extension()>
    Public Function ValueOrDefault(Of T)(token As JToken, defaultValue As T) As T
        If token IsNot Nothing AndAlso token.Type <> JTokenType.Null Then
            Return token.Value(Of T)
        Else
            Return defaultValue
        End If
    End Function

End Module

Then you can use it in wherever you are currently using Value(Of T) or DirectCast on a JToken. For example:

Dim brokers As JArray = item("broker").ValueOrDefault(new JArray())

Or:

Dim email As String = broker("emailaddress").ValueOrDefault("")
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • Thanks! You code works for `photos` array, but throws an error on `broker` property (see update 2)...could you help me explain why? – Adam Nov 24 '17 at 14:55
  • 1
    @Flo - I'm guessing the example in your question is just a shortened version of your "real" JSON? In the updated example you posted, there is no reason you should be getting that error; `broker` is just an array of length 1, as you suggested. But if you are running this code against a different, longer JSON and that JSON does not always have a `broker` for each result item, then that would result in the error you are seeing. I updated my answer to explain how you can fix it. – Brian Rogers Nov 24 '17 at 19:34
  • You were right...rookie mistake, but serendipity: I got a really nice extension method from it :-). One question on that: I have this in my JSON `"title": null`. When I use your extension method: `name = item("title").ValueOrDefault("")`, the code still fails since variable `name` will be `Nothing` instead of an empty string. How can I alter your extension to solve for that? – Adam Nov 24 '17 at 20:55
  • 1
    @Flo If the JSON contains an explicit null, we can check for that by looking at the `JToken.Type` property to see if it is `JTokenType.Null`. I've updated the code for the extension method in my answer. – Brian Rogers Nov 24 '17 at 21:11