-1

I have a problem to get out nested Values from JSON Objects. I Googled and came up with a small solution that delivers the "root" or let's say "unnested" values. The Deserialisation doesn't parse out the nested values. And now I am stuck.

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

JSON File Source (s):

{
  "matchNumber": "222",
  "phase": "Bronze Medal Match",
  "category": {
    "name": "F-67kg",
    "gender": "FEMALE",
    "subCategory": "OLYMPIC CATEGORY",
    "bodyLevel": 22,
    "headLevel": 5
  },
  "blueAthlete": {
    "name": "playernameblue",
    "wtfId": "wtfidblue",
    "flagAbbreviation": "GER"
  },
  "blueAthleteVideoQuota": 1,
  "redAthlete": {
    "name": "playernamered",
    "wtfId": "wtfidred",
    "flagAbbreviation": "AZE"
  },
  "redAthleteVideoQuota": 1,
  "roundsConfig": {
    "rounds": 3,
    "roundTimeMinutes": 2,
    "roundTimeSeconds": 0,
    "kyeShiTimeMinutes": 1,
    "kyeShiTimeSeconds": 0,
    "restTimeMinutes": 1,
    "restTimeSeconds": 0,
    "goldenPointEnabled": true,
    "goldenPointTimeMinutes": 1,
    "goldenPointTimeSeconds": 0
  },
  "differencialScore": 20,
  "maxAllowedGamJeoms": 0,
  "paraTkdMatch": false
}

Generated VB Code and Classes out of this JSON:

Public Class Category
    Public Property name As String
    Public Property gender As String 
    Public Property subCategory As String
    Public Property bodyLevel As String
    Public Property headLevel As String
End Class

Public Class BlueAthlete
    Public Property name As String
    Public Property wtfId As String
    Public Property flagAbbreviation As String
End Class

Public Class RedAthlete
    Public Property name As String
    Public Property wtfId As String
    Public Property flagAbbreviation As String
End Class

Public Class RoundsConfig
    Public Property rounds As Integer
    Public Property roundTimeMinutes As Integer
    Public Property roundTimeSeconds As Integer
    Public Property kyeShiTimeMinutes As Integer
    Public Property kyeShiTimeSeconds As Integer
    Public Property restTimeMinutes As Integer
    Public Property restTimeSeconds As Integer
    Public Property goldenPointEnabled As Boolean
    Public Property goldenPointTimeMinutes As Integer
    Public Property goldenPointTimeSeconds As Integer
End Class

Public Class RootObject
    Public Property matchNumber As String
    Public Property phase As String
    Public Property category As Category
    Public Property blueAthlete As BlueAthlete
    Public Property blueAthleteVideoQuota As Integer
    Public Property redAthlete As RedAthlete
    Public Property redAthleteVideoQuota As Integer
    Public Property roundsConfig As RoundsConfig
    Public Property differencialScore As Integer
    Public Property maxAllowedGamJeoms As Integer
    Public Property paraTkdMatch As Boolean
End Class

The Code I tried to Parse out the Values:

Dim rootObject As RootObject = JsonConvert.DeserializeObject(Of RootObject)(s)

String.Text = rootObject.matchNumber
String.Text = String.Text & vbCrLf & rootObject.phase

String.Text = String.Text & vbCrLf & rootObject.category.name
String.Text = String.Text & vbCrLf & rootObject.category.gender
String.Text = String.Text & vbCrLf & rootObject.category.subCategory
String.Text = String.Text & vbCrLf & rootObject.category.bodyLevel
String.Text = String.Text & vbCrLf & rootObject.category.headLevel

String.Text = String.Text & vbCrLf & rootObject.blueAthleteVideoQuota
String.Text = String.Text & vbCrLf & rootObject.redAthleteVideoQuota
String.Text = String.Text & vbCrLf & rootObject.differencialScore
String.Text = String.Text & vbCrLf & rootObject.maxAllowedGamJeoms
String.Text = String.Text & vbCrLf & rootObject.paraTkdMatch

The code returns matchNumber 222 and then a System.NullReferenceException occurs. Object not set to instance of Object

This row throws out the exception: String.Text = String.Text & vbCrLf & rootObject.category.name

in general all deeper nested values return that exception.

So the questions is, how to fix that?

Update: Here is the complete code for reproducing - So basicly this code is a simple server, retreiving the JSON data via a POST request. VAR jsonstring (renamed from s to jsonstring) contains the received JSON. Richtextbox1 also contains the JSON just for viewing that there is a JSON recieved. Richtextbox2 is for rowing up the values.

Imports System.Threading
Imports System.Net
Imports System.Text
Imports System.IO
Imports System
Imports System.Web
Imports System.Collections.Generic
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json.Converters
Imports Newtonsoft.Json.Serialization

Public Class Server

#Region "VARS"
    Private listener As HttpListener
    Private mainThread As Thread
    Dim encoding As New UTF8Encoding
    Public Shared JSON_Route As String = ""
    Dim jsonstring As String = ""
#End Region

    Protected Overrides Sub OnFormClosing(ByVal e As System.Windows.Forms.FormClosingEventArgs)
        listener.Abort()
        mainThread.Join()
    End Sub

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        mainThread = New Thread(AddressOf mainRequestLoop)
        mainThread.Start()
    End Sub

    Private Delegate Sub updateListBoxHandler(ByVal msg As String)

    Public Sub UpdateListBox(ByVal msg As String)
        If InvokeRequired Then
            Invoke(New updateListBoxHandler(AddressOf UpdateListBox), New String() {msg})
        Else
            ListBox1.Items.Add(msg)
        End If
    End Sub

    Public Class Category
        Public Property name As String
        Public Property gender As String
        Public Property subCategory As String
        Public Property bodyLevel As String
        Public Property headLevel As String
    End Class

    Public Class BlueAthlete
            Public Property name As String
            Public Property wtfId As String
            Public Property flagAbbreviation As String
        End Class

        Public Class RedAthlete
            Public Property name As String
            Public Property wtfId As String
            Public Property flagAbbreviation As String
        End Class

        Public Class RoundsConfig
            Public Property rounds As Integer
            Public Property roundTimeMinutes As Integer
            Public Property roundTimeSeconds As Integer
            Public Property kyeShiTimeMinutes As Integer
            Public Property kyeShiTimeSeconds As Integer
            Public Property restTimeMinutes As Integer
            Public Property restTimeSeconds As Integer
            Public Property goldenPointEnabled As Boolean
            Public Property goldenPointTimeMinutes As Integer
            Public Property goldenPointTimeSeconds As Integer
        End Class

        Public Class RootObject
            Public Property matchNumber As String
            Public Property phase As String
            Public Property category As Category
            Public Property blueAthlete As BlueAthlete
            Public Property blueAthleteVideoQuota As Integer
            Public Property redAthlete As RedAthlete
            Public Property redAthleteVideoQuota As Integer
            Public Property roundsConfig As RoundsConfig
            Public Property differencialScore As Integer
            Public Property maxAllowedGamJeoms As Integer
            Public Property paraTkdMatch As Boolean
        End Class

    Public Sub mainRequestLoop()
        listener = New HttpListener()
        listener.Prefixes.Add("http://localhost:" & TextBox_Port.Text & "/" & TextBox_Restfulpath.Text & "/")
        Try
            listener.Start()
        Catch ex As Exception
            MsgBox("Please start as Admin (with administration rights granted.)")
        End Try

        Try
            Do

                Dim ctx As HttpListenerContext = listener.GetContext()
                Dim worker As New HttpRequestWorker(ctx, Me)
                ' ToDo: use threadpool threads probably better
                Dim t As New Thread(AddressOf worker.ProcessRequest)
                Dim request = ctx.Request
                Dim body As Stream = request.InputStream
                Dim reader As New StreamReader(body, encoding)
                jsonstring = reader.ReadToEnd()

                Richtextbox1.Text = jsonstring

                Dim rootObject As RootObject = JsonConvert.DeserializeObject(Of RootObject)(s)

                RichTextBox2.Text = rootObject.matchNumber
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.phase

                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.category.name
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.category.gender
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.category.subCategory
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.category.bodyLevel
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.category.headLevel

                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.blueAthleteVideoQuota
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.redAthleteVideoQuota
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.differencialScore
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.maxAllowedGamJeoms
                RichTextBox2.Text = RichTextBox2.Text & vbCrLf & rootObject.paraTkdMatch


                t.Start()

            Loop
        Catch ex As Exception
            MsgBox(ex.ToString())
        End Try
    End Sub
    ' Http Request Handler
    Private Class HttpRequestWorker

        Private context As HttpListenerContext
        Private caller As Server

        Public Sub New(ByVal context As HttpListenerContext, ByVal f As Server)
            Me.context = context
            caller = f
        End Sub

        ' Handle the request
        Public Sub ProcessRequest()

            Dim msg As String = context.Request.HttpMethod & " " & context.Request.Url.ToString()
            JSON_Route = context.Request.Url.ToString()

            caller.UpdateListBox(msg)

            Dim url As System.Uri = context.Request.Url

            Dim path As String = url.GetComponents(UriComponents.Path, UriFormat.SafeUnescaped)
            Debug.WriteLine(path)

            Dim response As HttpListenerResponse = context.Response

            Try
                Dim requestError As Boolean
                Dim requestError1 As Boolean

                requestError = context.Request.HttpMethod.ToUpper() <> "GET" OrElse String.IsNullOrEmpty(path)
                requestError1 = context.Request.HttpMethod.ToUpper() <> "POST" OrElse String.IsNullOrEmpty(path)

                If Not requestError Then
                    response.AddHeader("Cache-Control", "no-cache")
                    response.AddHeader("Pragma", "no-cache")
                    response.StatusCode = 200

                    Dim encoding As New UTF8Encoding
                    response.ContentEncoding = encoding
                    response.ContentType = "text/html"

                    Dim responseHtml As String = "Available"
                    Dim responseHtmlBytes() As Byte = encoding.GetBytes(responseHtml)
                    response.ContentLength64 = responseHtmlBytes.Length

                    Dim stream As IO.Stream = response.OutputStream
                    stream.Write(responseHtmlBytes, 0, responseHtmlBytes.Length)
                    stream.Close()
                Else
                    response.StatusCode = 404
                End If
            Catch ex As Exception
                response.StatusCode = 500
            Finally
                response.Close()
            End Try
        End Sub
End Class
Flavia
  • 1
  • 6
  • I can give you a proper answer now, but do check some of my answers on JSON topics. Use some visualizer then write out the level names, imagine a json being a x story building and then you going up those floors one by one. – CruleD Jul 21 '19 at 14:34
  • i found your topic: https://stackoverflow.com/questions/56970361/parse-complex-json-data-with-vb-net-and-newtonsoft-json/56971877#56971877 - i will look into that. looks easy and simple. i will let you know later if that worked. – Flavia Jul 21 '19 at 14:51
  • That's one of them yes. – CruleD Jul 21 '19 at 14:51
  • 1
    What is String? You didn't name a String variable String, did you? Is it a textbox? If it is a text box, don't update the UI over and over; that's a lot of screen redraws. Build a string in code and at the end set the text property of the text box once. – Mary Jul 21 '19 at 17:27
  • I cannot reproduce your problem. Your code does not compile as-is because `String.Text` is not defined. If I change `String.Text` to a local variable, `Dim Text = rootObject.matchNumber`, then your code runs successfully without any error: https://dotnetfiddle.net/FEM4h8. Can you please [edit] your question to share a [mcve]? – dbc Jul 21 '19 at 18:10
  • *then a System.NullReferenceException occurs* - then absent a [mcve] this is a duplicate of [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/q/4660142/3744182). – dbc Jul 21 '19 at 18:11
  • But if `category` might be null you can always use the **[null conditional operator](https://stackoverflow.com/q/45470502/3744182)**: `String.Text & vbCrLf & rootObject.?category?.name`. See: [is there a null conditional operator in Vbnet?](https://stackoverflow.com/q/45470502/3744182). – dbc Jul 21 '19 at 18:28
  • Hey Mary, String.text is a Richtextbox. That works properly, and it shows me all values when i do not try to parse the deeper rootObject.category.xxx values. if i only parse the rootObject.yyy it works. – Flavia Jul 21 '19 at 20:56
  • dbc: thx for the explanation, so i think i get the exception because of rootObject.category.xxx was NULL, or at least it couldnt find the specific values to it, which are obviously there. String.txt is a richtextbox that only shows me the values at a glance. later i want to put every value into their own vars. – Flavia Jul 21 '19 at 21:04
  • @Flavia - If some of the properties might be null then see [is there a null conditional operator in Vbnet?](https://stackoverflow.com/q/45470502/3744182) and/or [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/q/4660142/3744182). I don't think we can provide additional help beyond pointing out those two questions without a [mcve] that demonstrates the problem. – dbc Jul 21 '19 at 21:09
  • @Flavia - incidentally, `Text = Text & Nothing` does not throw a NullReferenceException, so the problem can't be that the properties of `category` are null. See https://dotnetfiddle.net/FEM4h8 – dbc Jul 21 '19 at 21:15
  • @CruleD : i tried that simple parse job you posted. logicly i understand it. but it also throws out the exception as soon as it tries to get the value from rootObject.category.name - i tried following simple as your example: Dim Something as JObject = JObject.Parse(jsonstring) - RichTextBox2.Text = "category : " & CType(Something ("category")("name"), String) . when i try RichTextBox2.Text = "category : " & Something("category")("name") it says "Operator '&' is not defined for types 'String' and 'JTOKEN'" – Flavia Jul 22 '19 at 12:57
  • As an aside, it looks to me like your `RedAthlete` and `BlueAthlete` classes should be merged into a single `Athlete` (your `RedAthlete` and `BlueAthlete` members of `RootObject` would remain, they would just both change to be instances of `Athlete`). – Craig Jul 22 '19 at 14:01
  • @Craig thx for the tip, but that wont solve my problem – Flavia Jul 22 '19 at 16:27
  • I didn't expect it to, hence "as an aside." – Craig Jul 22 '19 at 17:18
  • That is correct, Something("category")("name") is a JToken and it has multiple properties, think of it like datagridview cell. Now when you have selected it, you have to say what you want to do, in this case you want value so simply do RichTextBox2.Text = "category : " & Something("category")("name").ToString and you get "category: F-67kg". Can you say what (value) are you trying to get, but aren't getting? – CruleD Jul 22 '19 at 18:32
  • @CruleD when i do as you say: RichTextBox2.Text = "category : " & Something("category")("name").ToString - Sadly it throws exception error: System.NullReferenceException occurs. Object not set to instance of Object – Flavia Jul 22 '19 at 21:34
  • With the json you provided and just 3 lines of code it works fine for me, as I've shown above. Check what you are missing then. – CruleD Jul 23 '19 at 04:00
  • @CruleD i updated - provided the full code. please check. i do the deserialisation within a do / loop. – Flavia Jul 23 '19 at 08:31
  • So you just want to display received JSON? – CruleD Jul 23 '19 at 11:14
  • @CruleD No, i definitly already can see the received JSON. But when i want to extract the nested values for category like name, gender, bodyLevel it throws exception. – Flavia Jul 23 '19 at 15:53
  • How do you see it if it gives you and error xD I didn't look why it doesn't work for you, but if you are doing what I told you it should work. Works just fine for me both ways. – CruleD Jul 23 '19 at 16:32
  • @All: i SOLVED the problem. thx to all for your effort. Here there explanation:since the deserialisation was in a DO/LOOP the JSON switched to another JSON and back.The server received 2 JSONS that stored different keys and values. so the deserialisation tried to get values from a key/obect that was not in the JSON. so instead of doing nothing and dismiss it, it threw a nullexception. I just made following: 1st i checked the JSON if it contains a specific term - if jsonstring.Contains("phase") - then it handles the deserialisation with the exact Classes given and it works like a charm. – Flavia Jul 23 '19 at 16:47

1 Answers1

0

Please review what you are doing wrong, works fine on my end both ways.

Imports System.Reflection
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Public Class Form1

    Public Class Category
        Public Property name As String
        Public Property gender As String
        Public Property subCategory As String
        Public Property bodyLevel As Integer
        Public Property headLevel As Integer
    End Class

    Public Class BlueAthlete
        Public Property name As String
        Public Property wtfId As String
        Public Property flagAbbreviation As String
    End Class

    Public Class RedAthlete
        Public Property name As String
        Public Property wtfId As String
        Public Property flagAbbreviation As String
    End Class

    Public Class RoundsConfig
        Public Property rounds As Integer
        Public Property roundTimeMinutes As Integer
        Public Property roundTimeSeconds As Integer
        Public Property kyeShiTimeMinutes As Integer
        Public Property kyeShiTimeSeconds As Integer
        Public Property restTimeMinutes As Integer
        Public Property restTimeSeconds As Integer
        Public Property goldenPointEnabled As Boolean
        Public Property goldenPointTimeMinutes As Integer
        Public Property goldenPointTimeSeconds As Integer
    End Class

    Public Class Example
        Public Property matchNumber As String
        Public Property phase As String
        Public Property category As Category
        Public Property blueAthlete As BlueAthlete
        Public Property blueAthleteVideoQuota As Integer
        Public Property redAthlete As RedAthlete
        Public Property redAthleteVideoQuota As Integer
        Public Property roundsConfig As RoundsConfig
        Public Property differencialScore As Integer
        Public Property maxAllowedGamJeoms As Integer
        Public Property paraTkdMatch As Boolean
    End Class

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim Something As JObject = JObject.Parse(TextBox1.Text)
        Debug.Print(String.Format("I {0} want {1} this {2}", Something("category")("name").ToString, Something("category")("gender").ToString, Something("category")("bodyLevel").ToString))
' I F-67kg want FEMALE this 22
        Dim testing As Example = JsonConvert.DeserializeObject(Of Example)(TextBox1.Text)
        Debug.Print(String.Format("I {0} want {1} this {2}", testing.category.name, testing.category.gender, testing.category.bodyLevel))
' I F-67kg want FEMALE this 22


    TextBox2.AppendText(testing.matchNumber & vbNewLine)
    TextBox2.AppendText(testing.phase & vbNewLine)
    TextBox2.AppendText(testing.category.name & vbNewLine)
    TextBox2.AppendText(testing.category.gender & vbNewLine)
    TextBox2.AppendText(testing.category.subCategory & vbNewLine)
    TextBox2.AppendText(testing.category.bodyLevel & vbNewLine)
    TextBox2.AppendText(testing.category.headLevel & vbNewLine)
    TextBox2.AppendText(testing.blueAthleteVideoQuota & vbNewLine)
    TextBox2.AppendText(testing.redAthleteVideoQuota & vbNewLine)
    TextBox2.AppendText(testing.differencialScore & vbNewLine)
    TextBox2.AppendText(testing.maxAllowedGamJeoms & vbNewLine)
    TextBox2.AppendText(testing.paraTkdMatch & vbNewLine)

    For Each item As PropertyInfo In testing.GetType.GetProperties()
        If item.PropertyType.IsNested Then

            Dim TypeHolder as Type = item.GetValue(testing)
            For Each subitem As PropertyInfo In TypeHolder.GetType.GetProperties()
                TextBox3.AppendText(subitem.Name & ": " & subitem.GetValue(TypeHolder).ToString & vbNewLine)
            Next

        Else
            TextBox3.AppendText(item.Name & ": " & item.GetValue(testing).ToString & vbNewLine)
        End If
    Next
    End Sub
End Class

enter image description here

CruleD
  • 1,153
  • 2
  • 7
  • 15