How to parse Json with 2 different keys?

Viewed 102

I'm trying to parse this API

that is basically an order book with "asks" and "bids". How can I parse them splitting the asks from the bids? for example the api start with asks property So if Json is {"asks":[["0.00001555","3264400"],["0.00001556","3662200"],["0.00001573","3264400"]

I'm expecting an output like:

[asks]

Price- Quantity

0.00001555 - 3264400

0.00001556 - 3662200

0.00001573 - 3264400

ecc

and After there is "bids":[["0.00001325","300"],["0.00001313","100"],["0.00001312","1051400"],["0.00001311","1300000"],["0.0000131","9336700"] so I'm expecting

[bids]

Price- Quantity

0.00001325- 300

0.00001313 - 100

0.00001312 - 1051400

0.00001311 - 1300000

0.0000131 - 9336700

ecc

I know how to parse each single value with the code:

 Dim streamData As Stream = Nothing
      Using http As HttpClient = New HttpClient
          Dim url As String = "https://api.hotbit.io/api/v1/order.depth?market=KIBA/USDT&limit=100&interval=1e-8"
        
          Dim t As Task(Of Stream) = http.GetStreamAsync(url)
          streamData = t.Result
      End Using
        
      Dim jsonResponse As JsonNode = JsonNode.Parse(streamData)
      Dim result As JsonObject = jsonResponse("result").AsObject
        
  For Each kvp In result.AsEnumerable
      c &= kvp.Value("key?!?!?!?").ToString & ", "
  Next

but in this case there is no Key that I can "parse" and also the values are in the format ["Price", "Quantity"] and also I don't know how to split the results between asks and bids maybe splitting them in two different richtextboxes multiline.. Any help would be appreciated Thanks

1 Answers

Use JsonUtils which will create the classes for you and then you can copy these into your project.

In this instance it will create:

Public Class Result
    Public Property asks As String()()
    Public Property bids As String()()
    Public Property seqNum As Integer
    Public Property prevSeqNum As Integer
    Public Property lastTs As Double
End Class

Public Class Root
    Public Property [error] As Object
    Public Property result As Result
    Public Property id As Integer
End Class

Please note having error as a property name will throw a compiler error and so you'll need to wrap it in square brackets like [error].

You can also simplify your code considerably:

Private Async Sub MyMethod()
    Dim root As Root = Await GetJsonFromApi()

    For each ask In root.result.asks
        Console.WriteLine(ask(0))
    Next
End Sub

Private Async Function GetJsonFromApi() As Task(Of Root)
    Using http As New HttpClient()
        Dim url As String = "https://api.hotbit.io/api/v1/order.depth?market=KIBA/USDT&limit=100&interval=1e-8"
        Return Await http.GetFromJsonAsync(Of Root)(url)
    End Using
End Function

Or if you prefer, and want to handle exceptions, something like:

Private Async Sub MyMethod()
    Dim root As Root = Await GetJsonFromApi()

    For each ask In root.result.asks

    Next
End Sub

Private Async Function GetJsonFromApi() As Task(Of Root)
    Using http As New HttpClient()
        Dim url As String = "https://api.hotbit.io/api/v1/order.depth?market=KIBA/USDT&limit=100&interval=1e-8"
        Dim response As HttpResponseMessage = Await http.GetAsync(url)

        If response.IsSuccessStatusCode 
            Return Await response.Content.ReadFromJsonAsync(Of Root)
        End If
    End Using

    Return New Result
End Function

As a side note, I'm awaiting the response as you can see with the keyword Await. I prefer to do this rather than call .Result on the end.

Lastly, I would consider making the url variable a constant:

Private Const URI As String = "https://api.hotbit.io/api/v1/order.depth?market=KIBA/USDT&limit=100&interval=1e-8"

...
Dim response As HttpResponseMessage = Await http.GetAsync(URI)

There is also Newtonsoft.Json which can be used to deserialise JSON to a class:

Private Async Function GetJsonFromApi() As Task(Of Root)
    Using http As New HttpClient(),
          response As HttpResponseMessage = await http.GetAsync(URI)
            Return JsonConvert.DeserializeObject(Of Root)(Await response.Content.ReadAsStringAsync())
    End Using
End Function

Note you will have to add this as a Nuget package to your application. Also I've assumed the URI is now a constant and I've nested the Using statements.

To output these you will have to use String.Join and to ensure the UI doesn't freeze you can call Control.BeginInvoke:

Private Sub OutputAsks(asks As String()())
    Dim contents As New StringBuilder

    contents.AppendLine("[asks]")
    contents.AppendLine("Price - Quantity")

    For Each ask In asks
        contents.AppendLine(String.Join(" - ", ask))
    Next

    RichTextBox1.BeginInvoke(Sub()
                                 RichTextBox1.Text = contents.ToString()
                             End Sub)
End Sub

Private Sub OutputBids(bids As String()())
    Dim contents As New StringBuilder

    contents.AppendLine("[bids]")
    contents.AppendLine("Price - Quantity")

    For Each bid In bids
        contents.AppendLine(String.Join(" - ", bid))
    Next

    RichTextBox2.BeginInvoke(Sub()
                                 RichTextBox2.Text = contents.ToString()
                             End Sub)
End Sub

Note, as part of the Control.BeginInvoke you don't have to strictly call Control.EndInvoke as noted in the docs:

You can call EndInvoke to retrieve the return value from the delegate, if neccesary, but this is not required. EndInvoke will block until the return value can be retrieved.

The full solution - taking into account your Button, Timer, and RichTextbox controls - would look something like :

Private Const URI As String = "https://api.hotbit.io/api/v1/order.depth?market=KIBA/USDT&limit=10&interval=1e-8"

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Timer1.Start()
End Sub

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    UpdateContents()
End Sub

Private Async Sub UpdateContents()
    Dim jsonContents As String = Await GetJsonFromApi()
    Dim root As Root = DeserialiseJsonToRoot(jsonContents)

    OutputAsks(root.result.asks)
    OutputBids(root.result.bids)
End Sub

Private Async Function GetJsonFromApi() As Task(Of String)
    Using http As New HttpClient(),
        response As HttpResponseMessage = Await http.GetAsync(URI)
        Return Await response.Content.ReadAsStringAsync()
    End Using
End Function

Private Function DeserialiseJsonToRoot(json As String) As Root
    Return JsonConvert.DeserializeObject(Of Root)(json)
End Function

Private Sub OutputAsks(asks As String()())
    Dim contents As New StringBuilder

    contents.AppendLine("[asks]")
    contents.AppendLine("Price - Quantity")

    For Each ask In asks
        contents.AppendLine(String.Join(" - ", ask))
    Next

    RichTextBox1.BeginInvoke(Sub()
                                 RichTextBox1.Text = contents.ToString()
                             End Sub)
End Sub

Private Sub OutputBids(bids As String()())
    Dim contents As New StringBuilder

    contents.AppendLine("[bids]")
    contents.AppendLine("Price - Quantity")

    For Each bid In bids
        contents.AppendLine(String.Join(" - ", bid))
    Next

    RichTextBox2.BeginInvoke(Sub()
                                 RichTextBox2.Text = contents.ToString()
                             End Sub)
End Sub
Related