Understanding JSON (revisited)

Hi all!

I’m trying JSON parsing for the first time and getting stuck. I’ve tried to do what Rick Araujo demonstrated in his reply to this post Understanding JSON but my JSON file has one less layer in it than his example. It’s less complex, but I’m stuck.

I need to get the post fields and make use of some of them.

My code so far is:

//json file is stored as string jsonTextString

var js as new JSONItem(jsonTextString)
var messages, data, postElements as JSONItem

messages = js.Value("messages")

var lastMessage as Integer = messages.LastRowIndex

for messageIndex as Integer = 0 to lastMessage
  
  data = messages(messageIndex)
  
  //here's where I get stuck
  
  
next

My JSON file is this:


{
 "type": "saved_messages",
 "id": 4747372822,
 "messages": [
  {
   "id": 161,
   "type": "service",
   "date": "2025-05-27T20:00:51",
   "date_unixtime": "1748401251",
   "actor": "John Smith",
   "actor_id": "user4747372822",
   "action": "clear_history",
   "text": "",
   "text_entities": []
  },
  {
   "id": 165,
   "type": "message",
   "date": "2025-05-27T20:25:31",
   "date_unixtime": "1748402731",
   "from": "John Smith",
   "from_id": "user4747372822",
   "forwarded_from": "Trade Watcher",
   "saved_from": "Trade Watcher",
   "photo": "photos/photo_1@27-05-2025_20-25-31.jpg",
   "photo_file_size": 83046,
   "width": 1179,
   "height": 671,
   "text": [
    {
     "type": "bold",
     "text": "BREAKING: President Trump says Putin is “playing with fire.”"
    },
    ""
   ],
   "text_entities": [
    {
     "type": "bold",
     "text": "BREAKING: President Trump says Putin is “playing with fire.”"
    },
    {
     "type": "plain",
     "text": ""
    }
   ]
  },
  {
   "id": 166,
   "type": "message",
   "date": "2025-05-27T20:25:31",
   "date_unixtime": "1748402731",
   "from": "John Smith",
   "from_id": "user4747372822",
   "forwarded_from": "Trade Watcher",
   "saved_from": "Trade Watcher",
   "photo": "photos/photo_2@27-05-2025_20-25-31.jpg",
   "photo_file_size": 79738,
   "width": 1200,
   "height": 676,
   "text": [
    {
     "type": "bold",
     "text": "Traders bet on Nvidia surge ahead of earnings"
    },
    "\n\nBullish sentiment builds as call options stack up around $145–$150 strikes, with ",
    {
     "type": "cashtag",
     "text": "$NVDA"
    },
    " closing at $131.29 on Friday.\n\nA $5.5B China-related charge may hit Q1, but traders are eyeing a potential 6.7% move — Nvidia rarely stays quiet on earnings."
   ],
   "text_entities": [
    {
     "type": "bold",
     "text": "Traders bet on Nvidia surge ahead of earnings"
    },
    {
     "type": "plain",
     "text": "\n\nBullish sentiment builds as call options stack up around $145–$150 strikes, with "
    },
    {
     "type": "cashtag",
     "text": "$NVDA"
    },
    {
     "type": "plain",
     "text": " closing at $131.29 on Friday.\n\nA $5.5B China-related charge may hit Q1, but traders are eyeing a potential 6.7% move — Nvidia rarely stays quiet on earnings."
    }
   ]
  },
  {
   "id": 167,
   "type": "message",
   "date": "2025-05-27T20:25:31",
   "date_unixtime": "1748402731",
   "from": "John Smith",
   "from_id": "user4747372822",
   "forwarded_from": "Trade Watcher",
   "saved_from": "Trade Watcher",
   "photo": "photos/photo_3@27-05-2025_20-25-31.jpg",
   "photo_file_size": 34855,
   "width": 751,
   "height": 736,
   "text": [
    {
     "type": "bold",
     "text": "$7.24T parked in money markets — record high"
    },
    "\n\nTotal assets in Money Market Funds have surged to an all-time high of $7.24 trillion. \n\nThis reflects growing investor caution and a strong preference for yield and liquidity amid market uncertainty."
   ],
   "text_entities": [
    {
     "type": "bold",
     "text": "$7.24T parked in money markets — record high"
    },
    {
     "type": "plain",
     "text": "\n\nTotal assets in Money Market Funds have surged to an all-time high of $7.24 trillion. \n\nThis reflects growing investor caution and a strong preference for yield and liquidity amid market uncertainty."
    }
   ]
  }
 ]
}

Looks like you’ve rightly identified messages as being a json array. But you’re not addressing its elements properly.

Try:

data = messages.ChildAt(messageIndex)
MessageBox data.Value("id").StringValue
2 Likes

Hello Bill,

If you put the code below behind a button and add 3 TextAreas (TextArea1, TextArea2, TextArea3), and place your json in TextArea1, then this should help you understand what’s going on.

Var jSource As New JSONItem(TextArea1.Text)
jSource.Compact = True
Var jMessages As JSONItem = jSource.Lookup("messages", New JSONItem)
Var jMessagesCount As Integer = jMessages.Count

// Messages (L1)
Var vId As String
Var vType As String
Var vDate As String
Var vDateUnixTime As String
Var vFrom As String
Var vFromId As String
Var vForwardedFrom As String
Var vSavedFrom As String
Var vPhoto As String
Var vPhotoFileSize As String
Var vWidth As String
Var vHeight As String

// Messages (L2)
Var vText As String


For i As Integer =  0 To (jMessagesCount-1)
  Var jThisMessage As JSONItem = jMessages.ChildAt(i)
  
  
  If jThisMessage.HasKey("id") Then
    vId = jThisMessage.Value("id").StringValue
  Else
    vId = ""
  End
  
  If jThisMessage.HasKey("type") Then
    vType = jThisMessage.Value("type")
  Else
    vType = ""
  End
  
  If jThisMessage.HasKey("date") Then
    vDate = jThisMessage.Value("date")
  Else
    vDate = ""
  End
  
  If jThisMessage.HasKey("date_unixtime") Then
    vDateUnixTime = jThisMessage.Value("date_unixtime")
  Else
    vDateUnixTime = ""
  End
  
  If jThisMessage.HasKey("from") Then
    vFrom = jThisMessage.Value("from")
  Else
    vFrom = ""
  End
  
  If jThisMessage.HasKey("from_id") Then
    vFromId = jThisMessage.Value("from_id")
  Else
    vFromId = ""
  End
  
  If jThisMessage.HasKey("forwarded_from") Then
    vForwardedFrom = jThisMessage.Value("forwarded_from")
  Else
    vForwardedFrom = ""
  End
  
  If jThisMessage.HasKey("saved_from") Then
    vSavedFrom = jThisMessage.Value("saved_from")
  Else
    vSavedFrom = ""
  End
  
  If jThisMessage.HasKey("photo") Then
    vPhoto = jThisMessage.Value("photo")
  Else
    vPhoto = ""
  End
  
  If jThisMessage.HasKey("photo_file_size") Then
    vPhotoFileSize = jThisMessage.Value("photo_file_size").StringValue
  Else
    vPhotoFileSize = ""
  End
  
  If jThisMessage.HasKey("width") Then
    vWidth = jThisMessage.Value("width").StringValue
  Else
    vWidth = ""
  End
  
  If jThisMessage.HasKey("height") Then
    vHeight = jThisMessage.Value("height").StringValue
  Else
    vHeight = ""
  End
  
  TextArea2.Text = TextArea2.Text + vId + " | " + vType  + " | " + vDate  + " | " + vDateUnixTime + " | " + vFrom  + " | " + vFromId  + " | " + vForwardedFrom + " | " + vSavedFrom + " | " + vPhoto + " | " + vPhotoFileSize + " | " + vWidth + " | " + vHeight + EndOfLine
  
  
  
  If jThisMessage.HasKey("text_entities") Then
    Var jTextEntities As JSONItem = jThisMessage.Lookup("text_entities", New JSONItem)
    Var jTextEntitiesCount As Integer = jTextEntities.Count
    
    For ii As Integer = 0 To (jTextEntitiesCount-1)
      
      Var jThisTextEntity As JSONItem = jTextEntities.ChildAt(ii)
      
      If jThisTextEntity.HasKey("text") Then
        vText = jThisTextEntity.Value("text")
        TextArea3.Text = TextArea3.Text + vId + " | Text: " + vText + EndOfLine
      Else
        vText = ""
        TextArea3.Text = TextArea3.Text + vId + " | Text: " + vText + EndOfLine
      End
    Next
  End
  
Next

Output should look like this:

I separated the message (meta data) from the lines of the message (as I was unsure of your real target).

I hope that helps.

Kind regards, Andrew

1 Like

@Bill_Sanders

In addition to what everyone said, it’s important to understand that JSON is a nested protocol. That is, JSON objects can be nested within other JSON objects.

There are two top-level types for JSON: an object and an array, denoted by curly braces {} and square braces [] respectively. Those two items can contain any number of these six types:

  • Objects {} - key value pairs where values are these six types
  • Arrays [] - comma delimited list of these six types
  • Strings
  • Numbers
  • Booleans
  • Nulls

For more information, go to the source

2 Likes

… in addition to what Greg said, unlike a database with consistent schema json records need not include every available node in the json schema, which is the reason I always check if a node is present when looping records using “HasKey”.

Kind regards, Andrew

2 Likes

Or just use the Lookup method if you want a default value… just like Dictionary.

1 Like

I prefere use dictionnary, It’s simpler

Var dico_01 As New Dictionary
Var dico_02 As   Dictionary
Var tx_JSON As String

ico_02 = New Dictionary
dico_02.Value ( "01" ) = "bonjou"r
dico_02.Value ( "02" ) = "au revoir"
dico_01.value ( "salut" ) = dico_01

dico_02 = New Dictionary
dico_02.Value ( "rouge" ) = Color.red.ToString
dico_02.Value ( "vert" ) = Color.Green.ToString
dico_01.value ( "couleur" ) = dico_01

tx_JSON = GenerateJSON ( dico_01 , True )
2 Likes

FWIW, JSONItem is a Dictionary under the hood, but it also does some type enforcement.

Thanks everyone. This was super helpful!

Bill