Jump to content

Help with Json, please


Innula Zenovka
 Share

You are about to reply to a thread that has been inactive for 475 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

I've never really looked at json before, but now I find I have to as part of my adventures with shared media -- essentially, I'm receiving data from a remote server in the form of json objects and I need, in the http_response event, to extract the contents of particular fields and then format them as entries in an HTML table I display on the prim.

How, using this example from https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON, would I pull out the contents of the "name" and "powers" fields for each superhero in the list?

{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower",
  "active": true,
  "members": [
    {
      "name": "Molecule Man",
      "age": 29,
      "secretIdentity": "Dan Jukes",
      "powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
    },
    {
      "name": "Madame Uppercut",
      "age": 39,
      "secretIdentity": "Jane Wilson",
      "powers": [
        "Million tonne punch",
        "Damage resistance",
        "Superhuman reflexes"
      ]
    },
    {
      "name": "Eternal Flame",
      "age": 1000000,
      "secretIdentity": "Unknown",
      "powers": [
        "Immortality",
        "Heat Immunity",
        "Inferno",
        "Teleportation",
        "Interdimensional travel"
      ]
    }
  ]
}

 

  • Thanks 1
Link to comment
Share on other sites

string sJson = "{\"squadName\":\"Super hero squad\",\"homeTown\":\"Metro City\",\"formed\":2016,\"secretBase\":\"Super tower\",\"active\":true,\"members\":[{\"name\":\"Molecule Man\",\"age\":29,\"secretIdentity\":\"Dan Jukes\",\"powers\":[\"Radiation resistance\",\"Turning tiny\",\"Radiation blast\"]},{\"name\":\"Madame Uppercut\",\"age\":39,\"secretIdentity\":\"Jane Wilson\",\"powers\":[\"Million tonne punch\",\"Damage resistance\",\"Superhuman reflexes\"]},{\"name\":\"Eternal Flame\",\"age\":1000000,\"secretIdentity\":\"Unknown\",\"powers\":[\"Immortality\",\"Heat Immunity\",\"Inferno\",\"Teleportation\",\"Interdimensional travel\"]}]}";

default
{
    state_entry()
    {
        integer iMember;
        string sMemberJson;
        iMember = 0;
        sMemberJson = llJsonGetValue(sJson, ["members", iMember]);   // Get first Entry
        while (sMemberJson != JSON_INVALID) {
            llOwnerSay("Member Name: " + llJsonGetValue(sMemberJson, ["name"]));
            ++iMember;    
            sMemberJson = llJsonGetValue(sJson, ["members", iMember]);   // Get next Entry
        }
    }

}

Output:

Member Name: Molecule Man
Member Name: Madame Uppercut
Member Name: Eternal Flame

Thanks for giving me something to do!!!

*edit* You could also do it with just a single llJsonGetValue() call, duplicated before and inside the loop like this (assuming you only needed the "name" field):

 sMemberName = llJsonGetValue(sJson, ["members", iMember, "name"]);   // Get first Entry / next Entry

The previous example becomes:

default
{
    state_entry()
    {
        integer iMember;
        string sMemberName;
        iMember = 0;
        sMemberName = llJsonGetValue(sJson, ["members", iMember, "name"]);  
        while (sMemberName != JSON_INVALID) {
            llOwnerSay("Member Name: " +sMemberName);
            ++iMember;    
            sMemberName = llJsonGetValue(sJson, ["members", iMember, "name"]);  
        }
    }

}

 

 

Edited by Love Zhaoying
Fixed the second comment
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Thanks!   Presumably if I add the names to a "names" list and then do the same thing with  the "powers" field, and, at least in theory, I should end up with two synchronised LSL lists, "names" and "powers"?

Another question:   the JSON I'm working with has several embedded arrays (I think that's the correct term).    For example, it contains a number of entries for a field called simply "items", each of which contains fields like "id", "name"", "artist", "album" and so on, but  also an array inside the items field for "genres", which also contains multiple items for "id" and "name".

How do I reference these for llJsonGetValues?   Presumably I use llJsonGetValue("items",iItem,""album") for the names of the albums.     How do I reference the "name" field in genres, bearing in mind a single album may belong to more than one genre?   I'm wanting to use something like "items.genres" or "genres.name" but I'm not all sure of what I'm doing here.  

Edited by Innula Zenovka
Link to comment
Share on other sites

@Innula Zenovka

if your data is unchanging, you could cherry pick the info you need mebbe?

( from your prev example ) in your http response event, you could do ...

 string firstArray = llJsonGetValue(body,["members", 0, "powers"]);
 llOwnerSay("data: \n" + firstArray );  // returns ["Radiation resistance","Turning tiny","Radiation blast"]

Edited by Xiija
  • Thanks 1
Link to comment
Share on other sites

55 minutes ago, Innula Zenovka said:

Thanks!   Presumably if I add the names to a "names" list and then do the same thing with  the "powers" field, and, at least in theory, I should end up with two synchronised LSL lists, "names" and "powers"?

Yes.  Personally, I stopped using lists COMPLETELY.

So, if I really only needed to "mine" the names and powers, I'd just create a new JSON with just the data I needed.

Example using JSON_ARRAYS with a single JSON_OBJECT in each array element:

{  // Outer JSON_OBJECT

  [  // JSON_ARRAY

    {"name":"name1", "power":"power1"},  // JSON_ARRAY entry 1 is a JSON_OBJECT

    {"name":"name2", "power":"power2"}, // JSON_ARRAY entry 2 is a JSON_OBJECT

  ]

}

..Lists within Lists..!

 

55 minutes ago, Innula Zenovka said:

Another question:   the JSON I'm working with has several embedded arrays (I think that's the correct term).    For example, it contains a number of entries for a field called simply "items", each of which contains fields like "id", "name"", "artist", "album" and so on, but  also an array inside the items field for "genres", which also contains multiple items for "id" and "name".

How do I reference these for llJsonGetValues?   Presumably I use llJsonGetValue("items",iItem,""album") for the names of the albums.     How do I reference the "name" field in genres, bearing in mind a single album may belong to more than one genre?   I'm wanting to use something like "items.genres" or "genres.name" but I'm not all sure of what I'm doing here.  

Perhaps if you showed me an example.

The key to it all is the second parameter on llJsonGetValue() - I call it "[specifiers]" - a list of specifiers for what you want to access in the list.

Is it for example: Artists, Albums, Album Details?

{  // Outer JSON_OBJECT

   [  // Array of Artists

    { "Artist": "Artist 1",  // JSON_OBJECT for Artist 1

     "Albums":

      [  // Array of Albums for Artist 1

        { "name":  "Album1", id": 123, "year": 1974 },

        { "name":  "Album2", id": 345, "year": 1983 }

     ]  // End of Albums for Artist 1

   },   // End of JSON_OBJECT for Artist 1

    { "Artist": "Artist 2",  // JSON_OBJECT for Artist 2

     "Albums":

      [  // Array of Albums for Artist 2

        { "name":  "Album3", id": 789, "year": 1999},

        { "name":  "Album4", id": 135,"year": 2000}

     ]  // End of Albums for Artist 2

   }   // End of JSON_OBJECT for Artist 2

   ]  // end of Artists Array

}

 

 

Edited by Love Zhaoying
Removed comma
Link to comment
Share on other sites

@Innula Zenovka, sorry the last example took so long!  I hope this is along the lines of your data, for matching your use-case.

Example with the above:


string sJson = "[{\"Artist\":\"Artist 1\",\"Albums\":[{\"name\":\"Album1\",\"id\":123,\"year\":1974},{\"name\":\"Album2\",\"id\":345,\"year\":1983}]},{\"Artist\":\"Artist 2\",\"Albums\":[{\"name\":\"Album3\",\"id\":789,\"year\":1999},{\"name\":\"Album4\",\"id\":135,\"year\":2000}]}]";

default
{
    state_entry()
    {
    
        integer iArtist;
        string sArtistName;
        iArtist = 0;
        sArtistName = llJsonGetValue(sJson, [iArtist, "Artist"]);  // Get first Artist
        while (sArtistName != JSON_INVALID) {

            llOwnerSay("Artist: " + sArtistName);

            integer iAlbum;
            string sAlbumJson;
            string sAlbumName;
            integer iAlbumYear;
            string sAlbumID;
            iAlbum = 0;
            sAlbumJson = llJsonGetValue(sJson, [iArtist, "Albums", iAlbum]);  // Get first Album
            while (sAlbumJson!=JSON_INVALID) {

                sAlbumName = llJsonGetValue(sAlbumJson, ["name"]);
                iAlbumYear =  (integer)llJsonGetValue(sAlbumJson, ["year"]);
                sAlbumID = llJsonGetValue(sAlbumJson, ["id"]);
                llOwnerSay("Album " + (string)(iAlbum+1) + ":  name = " + sAlbumName + ", year =  " + (string)iAlbumYear + " ID = " + sAlbumID);

                ++iAlbum;
                sAlbumJson = llJsonGetValue(sJson, [iArtist, "Albums", iAlbum]);  // Get next Album
            }
        
            ++iArtist;    
            sArtistName = llJsonGetValue(sJson, [iArtist, "Artist"]);  // Get next Album
        }

    }

}

Output:

Artist: Artist 1
Album 1:  name = Album1, year =  1974 ID = 123
Album 2:  name = Album2, year =  1983 ID = 345
Artist: Artist 2
Album 1:  name = Album3, year =  1999 ID = 789
Album 2:  name = Album4, year =  2000 ID = 135

 

Edited by Love Zhaoying
  • Thanks 1
Link to comment
Share on other sites

Here is what is in the JSON_OBJECT in the above example:

[
  {
    "Artist": "Artist 1",
    "Albums": [
      {
        "name": "Album1",
        "id": 123,
        "year": 1974
      },
      {
        "name": "Album2",
        "id": 345,
        "year": 1983
      }
    ]
  },
  {
    "Artist": "Artist 2",
    "Albums": [
      {
        "name": "Album3",
        "id": 789,
        "year": 1999
      },
      {
        "name": "Album4",
        "id": 135,
        "year": 2000
      }
    ]
  }
]

 

  • Thanks 1
Link to comment
Share on other sites

27 minutes ago, Love Zhaoying said:

Perhaps if you showed me an example.

Sure.   This has only one item, but there could be several.

{
{"itemsCount":1,
"pagesCount":1,
"items":[
	{"id":"a uuid",
	"name":"name of track",
	"artist":"name of artist",
	"album":"name of album",
	"duration":"playing time",
	"genres":[
		{"id":12,
		"name":"Soul/R&B"}],
	"image":{"preview":"url of album cover"},
	"price":n,
	"currency":
		{"id":4,"name":"name of currency",
		"imageUrl":"url for currency symbol"},
	"status":"PURCHASED",
	"isNew":true,
	"isPreviewAvailable":true}
]}
}

So I want, for example,  to reference each name in items and, along with that, each name in genres for that item.

Link to comment
Share on other sites

25 minutes ago, Innula Zenovka said:

So I want, for example,  to reference each name in items and, along with that, each name in genres for that item.

I had to make one change to your example JSON, put quotes around the "n" for "price".

Note: Loops would go after each place where I set "variable = 0".

string sJson = "{\"itemsCount\":1,\"pagesCount\":1,\"items\":[{\"id\":\"a uuid\",\"name\":\"name of track\",\"artist\":\"name of artist\",\"album\":\"name of album\",\"duration\":\"playing time\",\"genres\":[{\"id\":12,\"name\":\"Soul/R&B\"}],\"image\":{\"preview\":\"url of album cover\"},\"price\":\"n\",\"currency\":{\"id\":4,\"name\":\"name of currency\",\"imageUrl\":\"url for currency symbol\"},\"status\":\"PURCHASED\",\"isNew\":true,\"isPreviewAvailable\":true}]}";

default
{
    state_entry()
    {
            
        integer iItem = 0;
    
        llOwnerSay("For iItem = " + (string)iItem + ":");
        

        string sName;
        sName = llJsonGetValue(sJson, ["items", iItem, "name"]);

        integer iGenre = 0;
        llOwnerSay("For iGenre = " + (string)iGenre + ":");

        string sGenre;        
        sGenre = llJsonGetValue(sJson, ["items", iItem, "genres", iGenre, "name"]);

        llOwnerSay("Name = " + sName + ", Genre = " + sGenre);

    }

}

 

Edited by Love Zhaoying
Oops, replaced one "0" with iGenre
  • Thanks 1
Link to comment
Share on other sites

42 minutes ago, Innula Zenovka said:
{
{"itemsCount":1,
"pagesCount":1,
"items":[
	{"id":"a uuid",
	"name":"name of track",
	"artist":"name of artist",
	"album":"name of album",
	"duration":"playing time",
	"genres":[
		{"id":12,
		"name":"Soul/R&B"}],
	"image":{"preview":"url of album cover"},
	"price":n,
	"currency":
		{"id":4,"name":"name of currency",
		"imageUrl":"url for currency symbol"},
	"status":"PURCHASED",
	"isNew":true,
	"isPreviewAvailable":true}
]}
}

This specifically seems to be invalid JSON, if you're getting something like this from your server, you'll need to remove the outermost set of curly braces.

Cleaning up the formatting of the JSON a little to make it clearer and adding more than one element per array might make things clearer. maybe stepping through the JSON one level at a time might make things clearer before trying to write a general routine?

string JSON = 
"{
    \"itemsCount\":1,
    \"pagesCount\":1,
    \"items\":
    [
        {
            \"id\":\"a uuid\",
            \"name\":\"name of track\",
            \"artist\":\"name of artist\",
            \"album\":\"name of album\",
            \"duration\":\"playing time\",
            \"genres\":
            [
                {
                    \"id\":12,
                    \"name\":\"Soul/R&B\"
                },
                {   
                    \"id\":13,
                    \"name\":\"Funk\"
                },
            ],
            \"image\":
            {   \"preview\":\"url of album cover\"
            },
            \"price\":n,
            \"currency\":
            {
                \"id\":4,\"name\":\"name of currency\",
                \"imageUrl\":\"url for currency symbol\"
            },
            \"status\":\"PURCHASED\",
            \"isNew\":true,
            \"isPreviewAvailable\":true
        },
        {   \"id\":\"another uuid\",
            \"name\":\"name of a different track\",
            \"artist\":\"name of other artist\",
            \"album\":\"name of different album\",
            \"duration\":\"playing time\",
            \"genres\":
            [
                {
                    \"id\":12,
                    \"name\":\"Soul/R&B\"
                },
                {   \"id\":13,
                    \"name\":\"Funk\"
                },
            ],
            \"image\":
            {   \"preview\":\"url of album cover\"
            },
            \"price\":n,
            \"currency\":
            {   \"id\":4,
                \"name\":\"name of currency\",
                \"imageUrl\":\"url for currency symbol\"
            },
            \"status\":\"PURCHASED\",
            \"isNew\":true,
            \"isPreviewAvailable\":true
        }
    ]
}
";

string json_type2string(string s)
{
    if(s==JSON_OBJECT)
    {   return "JSON_OBJECT";
    }else if(s==JSON_ARRAY)
    {   return "JSON_ARRAY";
    }else if(s==JSON_NUMBER)
    {   return "JSON_NUMBER";
    }else if(s==JSON_STRING)
    {   return "JSON_STRING";
    }else if(s==JSON_NULL)
    {   return "JSON_NULL";
    }else if(s==JSON_TRUE)
    {   return "JSON_TRUE";
    }else if(s==JSON_FALSE)
    {   return "JSON_FALSE";
    }else if(s==JSON_DELETE)
    {   return "JSON_DELETE";
    }
    
    return "JSON_INVALID";
    
}
default
{
    state_entry()
    {
        llOwnerSay(llJsonGetValue(JSON,["items"])); // returns a JSON_ARRAY. (surrounded by '[]') 
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items"])));
        llOwnerSay("\n----\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0])); // the 0th element of the array. is a JSON_OBJECT (surrounded by '{}')
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0])));
        llOwnerSay("\n----\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0,"genres"])); //  returns another JSON_ARRAY.
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0,"genres"])));
        llOwnerSay("\n----\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0,"genres",0])); // returns a JSON_OBJECT
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0,"genres",0])));
        llOwnerSay("\n----\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0,"genres",0,"name"])); // returns a string: Soul/R&B
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0,"genres",0,"name"])));
        llOwnerSay("\n---\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0,"genres",1,"name"])); // returns a string: Funk
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0,"genres",1,"name"])));
        llOwnerSay("\n---\n");
        
        llOwnerSay(llJsonGetValue(JSON,["items",0,"genres",2,"name"])); // returns JSON_INVALID (because past end of Array)
        llOwnerSay(json_type2string(llJsonValueType(JSON,["items",0,"genres",2,"name"])));
        llOwnerSay("\n---\n");
    }
}

Big idea, for a JSON_OBJECT, you get something inside by asking for one of the keys of a key-vlaue pair (I'm not actually sure how you would get the keys to ask for without knowing something about the format of the JSON a-priori, or converting the JSON_OBJECT to a strided list with llJson2List). for a JSON_ARRAY you get something inside by asking for an index, and to iterate, you ask for indexes starting at 0 and stopping when you get JSON_INVALID as an answer.

  • Thanks 1
Link to comment
Share on other sites

2 minutes ago, Quistess Alpha said:

This specifically seems to be invalid JSON, if you're getting something like this from your server, you'll need to remove the outermost set of curly braces.

JsonEditorOnline was not my friend with that last example.  Think I had to remove the outer {} AND change an n to a "n".

  • Like 1
Link to comment
Share on other sites

4 minutes ago, Quistess Alpha said:

for a JSON_ARRAY you get something inside by asking for an index, and to iterate, you ask for indexes starting at 0 and stopping when you get JSON_INVALID as an answer.

It's such a shame you can't get a count directly for arrays, and have to iterate (or convert the array to a list) to get the count.

Otherwise, the Object / Array combination works well for me!

  • Like 1
Link to comment
Share on other sites

1 minute ago, Love Zhaoying said:

It's such a shame you can't get a count directly for arrays, and have to iterate (or convert the array to a list) to get the count.

If I'm understanding this example correctly, I think "itemsCount" was intended to be the length of the array contained in items, but yeah, a shame.

If you ~really just needed the length of the array without iterating, for a sufficiently large array it might be possible to play hot and cold to get the length more efficiently (I.E. is there an element at position 1? yes -> 2? yes-> 4?yes ->8? yes-> 16? -> no 12? no -> 10? yes -> 11? yes -> list has 11 elements (8 checks) )

  • Like 1
Link to comment
Share on other sites

28 minutes ago, Quistess Alpha said:

f you ~really just needed the length of the array without iterating, for a sufficiently large array it might be possible to play hot and cold to get the length more efficiently (I.E. is there an element at position 1? yes -> 2? yes-> 4?yes ->8? yes-> 16? -> no 12? no -> 10? yes -> 11? yes -> list has 11 elements (8 checks) )

By total coincidence, my current "proof of concept" for the big JSON project is a "number guessing game".  I am going to train the program to also be a "guesser", and it seems that a combination of using "prime numbers" plus the "divide by 2 and pick a side closer/farther randomly" works best for human guessing.

  • Like 1
Link to comment
Share on other sites

@Innula Zenovka

Quote

How do I reference the "name" field in genres

 

 made a quickie json sender with your info for testing ... https://replit.com/@TikiHed/SL-json-responder#index.js

JSON sender ( the JSON )

you can make an http request to .. "https://SL-json-responder.tikihed.repl.co/input" .

I changed the JSON a bit, not sure if you are pulling json from somewhere

or you are making it,  but the 'music.json'  file in the node.js repl (above) has the format i used.

with that, i could do...

 string jdata = llJsonGetValue( body, ["items", "genres", 0, "name" ]);
 llOwnerSay("data: \n" + jdata );

 

Edited by Xiija
  • Thanks 1
Link to comment
Share on other sites

Thanks, everyone!  This is a huge help, and I'm beginning to understand what I'm doing with JSON, at least in this case.

Another question, though.   In the example I'm using, what I've got for "image" is 

"image":{

"preview":"some url",

"origin":"another url"
},

That is, it's a JSON object inside the "item" object, containing two values, and that's how Quistess' example recognises it.

How do I reference the values inside this embedded object for "preview" and "origin" respectively?    

 

Link to comment
Share on other sites

26 minutes ago, Innula Zenovka said:

Thanks, everyone!  This is a huge help, and I'm beginning to understand what I'm doing with JSON, at least in this case.

Another question, though.   In the example I'm using, what I've got for "image" is 

"image":{

"preview":"some url",

"origin":"another url"
},

That is, it's a JSON object inside the "item" object, containing two values, and that's how Quistess' example recognises it.

How do I reference the values inside this embedded object for "preview" and "origin" respectively?    

 

Just add image, preview or image, origin to the [specfiers] list parameter you'd pass to llJsonGetValue for any "higher level" fields in the same JSON. 

If I have a known starting point in the JSON, I'll often use a "base" list variable for that, and add a list for additional levels like this:

list lBase; // higher level

list lTarget.

lBase = [whatever];

example:

lTarget = ["image", "preview"];

string sURL;

sURL = llJsonGetValue(sourcejson, lBase + lTarget);

Each entry in the [specifiers] list just goes one level deeper into the JSON whether it's into a JSON_OBJECT or into a JSON_ARRAY.

  • Thanks 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 475 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...