Difference between revisions of "Talk:Json usage in LSL"
(Updated performance information with summary) |
m (1. Replaced very obsolete <lsl> tag with <syntaxhighlight>; the comments were impossible to read otherwise; 2. Added a lot of internal links; 3. Used more templates where appropriate (e.g. {{jira|}}; 4. Minor editing of some LSL scripts to conform to the universal style used throughout this wiki; 5. JSON is all-caps (not "json" nor "Json"!) no matter how LL uses it on their own function names.) |
||
Line 1: | Line 1: | ||
I am concerned because the JSON format specifies at json.org that you can use escape codes like \u0000 to represent Unicode byte values in strings. But LSL has the ugly habit of censoring and altering strings so that a character with byte value of 0x0000 is removed from the string, and some functions like llSHA1String are essentially broken since they also convert UTF-16 integers between \u0128–\u0255 into UTF-8 byte values starting with %c2 (which therefore have a different integer value due to the addition of the extraneous byte). Is LSL also going to mangle JSON strings' byte values when it renders them into LSL strings? Won't this corrupt attempts at efficiently verifying the signatures of any incoming messages, and thwart attempts to generate proper signatures for some outgoing JSON-formatted requests? Or do the Lindens have plans to finally give us a proper suite of escape codes in LSL (or some other solution)? | I am concerned because the JSON format specifies at [https://json.org json.org] that you can use escape codes like {{code|\u0000}} to represent Unicode byte values in strings. But LSL has the ugly habit of censoring and altering strings so that a character with byte value of {{code|0x0000}} is removed from the string, and some functions like [[llSHA1String]] are essentially broken since they also convert UTF-16 integers between {{code|\u0128–\u0255}} into UTF-8 byte values starting with {{code|%c2}} (which therefore have a different integer value due to the addition of the extraneous byte). Is LSL also going to mangle JSON strings' byte values when it renders them into LSL strings? Won't this corrupt attempts at efficiently verifying the signatures of any incoming messages, and thwart attempts to generate proper signatures for some outgoing JSON-formatted requests? Or do the Lindens have plans to finally give us a proper suite of escape codes in LSL (or some other solution)? | ||
--[[User:Gistya Eusebio|Gistya Eusebio]] 09:41, 30 May 2013 (PDT) | --[[User:Gistya Eusebio|Gistya Eusebio]] 09:41, 30 May 2013 (PDT) | ||
Line 20: | Line 20: | ||
== Unsure of major edit == | == Unsure of major edit == | ||
After making the edit on 16:04, 15 August 2013 to correct code examples that wouldn't compile, I've come to realize that the given examples, as well as the surrounding text, is in error since | After making the edit on 16:04, 15 August 2013 to correct code examples that wouldn't compile, I've come to realize that the given examples, as well as the surrounding text, is in error since JSON disallows empty values, as one might find in a "sparse" array. | ||
The author is pointing out "a rare exception" but then uses "{\"parent\":,}" and "{\"parent\":[ , , , , ]}" to illustrate. Neither of those can be arrived at with llList2Json(), since LSL doesn't allow empty list elements, but can only be obtained by hand coding to arrive at the non-compliant | The author is pointing out "a rare exception" but then uses {{code|"{\"parent\":,}"}} and {{code|"{\"parent\":[ , , , , ]}"}} to illustrate. Neither of those can be arrived at with [[llList2Json]](), since LSL doesn't allow empty list elements, but can only be obtained by hand coding to arrive at the non-compliant JSON strings. | ||
I feel that whole section should be rewritten to simply point out to the reader that they are advised to use llList2Json() in the formation of | I feel that whole section should be rewritten to simply point out to the reader that they are advised to use [[llList2Json]]() in the formation of JSON strings and avoid hand coding, which may well result in mal-formed constructions that could lead to confusing results in later operations. | ||
However, doing so would excise a number of paragraphs and I'm unsure how that would set with the original author and others. Guidance on this would be appreciated. [[User:LepreKhaun Resident|LepreKhaun Resident]] 05:25, 18 August 2013 (PDT) | However, doing so would excise a number of paragraphs and I'm unsure how that would set with the original author and others. Guidance on this would be appreciated. [[User:LepreKhaun Resident|LepreKhaun Resident]] 05:25, 18 August 2013 (PDT) | ||
Line 32: | Line 32: | ||
I want to submit a simple, working example on LSL JSON, but I don't know the right place for it | I want to submit a simple, working example on LSL JSON, but I don't know the right place for it | ||
:I hope Strife will and can place it, here it is: | :I hope Strife will and can place it, here it is: | ||
< | <syntaxhighlight lang="lsl2"> | ||
// JSON array forum example by Dora Gustafson, Studio Dora 2013 | // JSON array forum example by Dora Gustafson, Studio Dora 2013 | ||
// Building | // Building a 3 by 5 array in a JSON Object | ||
// Rows are indexed by name and columns are indexed by number = 0,1,2,3,4 | // Rows are indexed by name and columns are indexed by number = 0,1,2,3,4 | ||
string JSONVotes; | string JSONVotes; | ||
tellVotes( string voter) | tellVotes(string voter) | ||
{ | { | ||
string Js = llJsonGetValue( JSONVotes, [voter]); | string Js = llJsonGetValue(JSONVotes, [voter]); | ||
list Jl = llParseString2List( Js,[",","[","]","\""],[]); | list Jl = llParseString2List(Js, [",", "[", "]", "\""], []); | ||
string output = llDumpList2String( Jl, ", "); | string output = llDumpList2String(Jl, ", "); | ||
llOwnerSay( "Votes from "+voter+" are: "+output); | llOwnerSay("Votes from " + voter + " are: "+output); | ||
} | } | ||
Line 51: | Line 51: | ||
state_entry() | state_entry() | ||
{ // Building the JSON object | { // Building the JSON object | ||
string votes = llList2Json( JSON_ARRAY, [0, 0, 0, 0, 0]); // one row | string votes = llList2Json(JSON_ARRAY, [0, 0, 0, 0, 0]); // one row | ||
JSONVotes = llList2Json( JSON_OBJECT, [ "Betty", votes, "Jerry", votes, "Pierre", votes]); // complete object | JSONVotes = llList2Json(JSON_OBJECT, ["Betty", votes, "Jerry", votes, "Pierre", votes]); // complete object | ||
} | } | ||
touch_end( integer num) | touch_end( integer num) | ||
{ // Testing the JSON object | { // Testing the JSON object | ||
tellVotes( "Betty"); | tellVotes("Betty"); | ||
tellVotes( "Jerry"); | tellVotes("Jerry"); | ||
tellVotes( "Pierre"); | tellVotes("Pierre"); | ||
// saving some random votes | // saving some random votes | ||
JSONVotes = llJsonSetValue( JSONVotes, ["Betty", 1], (string)llFrand( 100.0)); | JSONVotes = llJsonSetValue( JSONVotes, ["Betty", 1], (string)llFrand(100.0)); | ||
JSONVotes = llJsonSetValue( JSONVotes, ["Jerry", 4], (string)llFrand( 100.0)); | JSONVotes = llJsonSetValue( JSONVotes, ["Jerry", 4], (string)llFrand(100.0)); | ||
// testing | // testing | ||
tellVotes( "Betty"); | tellVotes("Betty"); | ||
tellVotes( "Jerry"); | tellVotes("Jerry"); | ||
tellVotes( "Pierre"); | tellVotes("Pierre"); | ||
// getting one vote, example | // getting one vote, example | ||
string s = llJsonGetValue( JSONVotes, ["Betty",1]); | string s = llJsonGetValue(JSONVotes, ["Betty", 1]); | ||
llOwnerSay( "Betty votes "+s+" in second column"); | llOwnerSay("Betty votes " + s + " in second column"); | ||
} | } | ||
}</ | }</syntaxhighlight>[[User:Dora Gustafson|Dora Gustafson]] 06:23, 25 August 2013 (PDT) | ||
== Inconsistent behavior on types == | == Inconsistent behavior on types == | ||
The documentation says: if the specifier list element is an integer, then the corresponding element in the | The documentation says: if the specifier list element is an integer, then the corresponding element in the JSON value must be an array and the list element is used as a zero-based index into the JSON array. | ||
However, placing integers in a specifier used with a JSON_OBJECT and llJsonGetValue, they are automatically converted to strings. llList2Json and llJsonSetValue do not automatically convert integers to strings. Both will result in a JSON_INVALID if you try to put integers on the key side of a JSON_OBJECT. | However, placing integers in a specifier used with a [[JSON_OBJECT]] and [[llJsonGetValue]], they are automatically converted to strings. [[llList2Json]] and [[llJsonSetValue]] do not automatically convert integers to strings. Both will result in a [[JSON_INVALID]] if you try to put integers on the key side of a [[JSON_OBJECT]]. | ||
This example code, according to the documentation shouldn't even work (I'd presume it's supposed to return a JSON_INVALID): | This example code, according to the documentation shouldn't even work (I'd presume it's supposed to return a [[JSON_INVALID]]): | ||
< | <syntaxhighlight lang="lsl2">string test = llList2Json(JSON_OBJECT, ["1", "one", "2", "two", "3", "three"]); | ||
llOwnerSay(llJsonGetValue(test, [3]));</ | llOwnerSay(llJsonGetValue(test, [3]));</syntaxhighlight> | ||
This little sniplet produces "three". Additionally, using llJsonValueType in place of llJsonGetValue in the above example will result in JSON_STRING. Attempting something like 'llJsonSetValue(test, [3], "three");' results in JSON_INVALID.<br><br> | This little sniplet produces "three". Additionally, using [[llJsonValueType]] in place of [[llJsonGetValue]] in the above example will result in [[JSON_STRING]]. Attempting something like {{code|'llJsonSetValue(test, [3], "three");'}} results in [[JSON_INVALID]].<br><br> | ||
[[USER:Chetar Ruby|Chetar Ruby]] 09:15, 14 October 2013 (PDT) | [[USER:Chetar Ruby|Chetar Ruby]] 09:15, 14 October 2013 (PDT) | ||
: Yes, that's because what you meant to say was <code>llJsonSetValue(test, ["3"], "three")</code>– objects in LSL JSON land are always keyed on STRINGS– you can't pass numbers even when the string looks like a number. -- [[User:Winter Seale|Winter Seale]] 12:35, 23 January 2014 (PST) | : Yes, that's because what you meant to say was <code>llJsonSetValue(test, ["3"], "three")</code>– objects in LSL JSON land are always keyed on STRINGS– you can't pass numbers even when the string looks like a number. -- [[User:Winter Seale|Winter Seale]] 12:35, 23 January 2014 (PST) | ||
::Right, but you're overlooking what was being pointed out, a string isn't being required using llJsonGetValue() on a JSON object. It's an anomaly, though I can't see how this might adversely affect anything. [[User:LepreKhaun Resident|LepreKhaun Resident]] 14:29, 24 January 2014 (PST) | ::Right, but you're overlooking what was being pointed out, a string isn't being required using [[llJsonGetValue]]() on a JSON object. It's an anomaly, though I can't see how this might adversely affect anything. [[User:LepreKhaun Resident|LepreKhaun Resident]] 14:29, 24 January 2014 (PST) | ||
== Major change to the last part == | == Major change to the last part == | ||
I've made a major change per | I've made a major change per {{jira|BUG-6280}}; the change is http://wiki.secondlife.com/w/index.php?title=Json_usage_in_LSL&diff=1191136&oldid=1188019 - can someone please review it for accuracy? --[[User:Sei Lisa|Sei Lisa]] 18:09, 6 June 2014 (PDT) | ||
:Review is not the same as undo. I expected some commentary on my edit, not a full reversal. Why is there an example using undocumented features, and why has the explanatory text that goes with the example been removed? --[[User:Sei Lisa|Sei Lisa]] 11:21, 9 June 2014 (PDT) | :Review is not the same as undo. I expected some commentary on my edit, not a full reversal. Why is there an example using undocumented features, and why has the explanatory text that goes with the example been removed? --[[User:Sei Lisa|Sei Lisa]] 11:21, 9 June 2014 (PDT) | ||
:I'm reverting Miranda's changes for several reasons. | :I'm reverting Miranda's changes for several reasons. | ||
:1) The justification for the removal of the paragraph is redundancy. Nowhere in the article explains how the strings are interpreted when they aren't quoted inside the string itself (like the LSL string "\"true\""). The text merely says: "LSL strings which both begin and end with "'''\"'''" are interpreted literally as JSON strings, while those without are parsed when converted into JSON." But it does not explain what it means by parsed. It's very ambiguous, and it's not clear at all from that how it acts. The addition of a text that explains how the strings "true", "false" and "null" behave is therefore NOT redundant. | :1) The justification for the removal of the paragraph is redundancy. Nowhere in the article explains how the strings are interpreted when they aren't quoted inside the string itself (like the LSL string {{code|"\"true\""}}). The text merely says: "LSL strings which both begin and end with "'''\"'''" are interpreted literally as JSON strings, while those without are parsed when converted into JSON." But it does not explain what it means by parsed. It's very ambiguous, and it's not clear at all from that how it acts. The addition of a text that explains how the strings "true", "false" and "null" behave is therefore NOT redundant. | ||
:2) There's no example of how to properly encode strings. Having an example that omits the quotes around the string values may lead to think that that's the norm, when that is undocumented behavior (nowhere in the article says what llList2Json or llJsonSetValue actually do with strings that don't decode to any valid JSON type - they happen to be translated to JSON strings, but some of these strings like "true", "false", "[]", etc. will not, and that's not exaplained). | :2) There's no example of how to properly encode strings. Having an example that omits the quotes around the string values may lead to think that that's the norm, when that is undocumented behavior (nowhere in the article says what [[llList2Json]] or [[llJsonSetValue]] actually do with strings that don't decode to any valid JSON type - they happen to be translated to JSON strings, but some of these strings like "true", "false", "[]", etc. will not, and that's not exaplained). | ||
:3) The paragraph was added in this edit: http://wiki.secondlife.com/w/index.php?title=Json_usage_in_LSL&diff=1179766&oldid=1179765 - note how the example, initially written by Maestro, was buggy to the point that it used TRUE instead of JSON_TRUE, therefore the explanatory text was not redundant. It was merely not totally accurate, and I believe my edits made it clearer. | :3) The paragraph was added in this edit: http://wiki.secondlife.com/w/index.php?title=Json_usage_in_LSL&diff=1179766&oldid=1179765 - note how the example, initially written by Maestro, was buggy to the point that it used TRUE instead of [[JSON_TRUE]], therefore the explanatory text was not redundant. It was merely not totally accurate, and I believe my edits made it clearer. | ||
:--[[User:Sei Lisa|Sei Lisa]] 11:58, 9 June 2014 (PDT) | :--[[User:Sei Lisa|Sei Lisa]] 11:58, 9 June 2014 (PDT) | ||
Line 105: | Line 105: | ||
This section looks factually incorrect: | This section looks factually incorrect: | ||
<blockquote style="border:solid 1px silver;background:#FAFAF8;padding:4px"> | <blockquote style="border:solid 1px silver;background:#FAFAF8;padding:4px"> | ||
If, anywhere along the line, any of the specified nodes do not exist, [[llJsonGetValue]] and [[llJsonValueType]] will almost always return [[JSON_INVALID]]. However there is a rare exception: if any specifier in the list leads to a node that has no characters in its JSON string value, [[JSON_NULL]] will be returned instead of [[JSON_INVALID]] | If, anywhere along the line, any of the specified nodes do not exist, [[llJsonGetValue]] and [[llJsonValueType]] will almost always return [[JSON_INVALID]]. However there is a rare exception: if any specifier in the list leads to a node that has no characters in its JSON string value, [[JSON_NULL]] will be returned instead of [[JSON_INVALID]] — no matter what might come after it in the specifiers list. For example in: | ||
< | <syntaxhighlight lang="lsl2"> | ||
string example = "{\"parent\":,}"; | string example = "{\"parent\":,}"; | ||
string test1 = llJsonGetValue(example,["parent"]); | string test1 = llJsonGetValue(example, ["parent"]); | ||
string test2 = llJsonGetValue(example,["parent | string test2 = llJsonGetValue(example, ["parent that doesn't exist", "etc."]); | ||
</ | </syntaxhighlight> | ||
test1 and test2 will both be [[JSON_NULL]]. | {{code|test1}} and {{code|test2}} will both be [[JSON_NULL]]. | ||
The same is true for arrays: a specifier pointing to any element of a valid array will return [[JSON_NULL]] anywhere there is no JSON value specified at given position in the array: | The same is true for arrays: a specifier pointing to any element of a valid array will return [[JSON_NULL]] anywhere there is no JSON value specified at given position in the array: | ||
< | <syntaxhighlight lang="lsl2"> | ||
string example = "{\"parent\":[ , , , , ]}"; | string example = "{\"parent\":[ , , , , ]}"; | ||
string test1 = llJsonGetValue(example,["parent",2]); | string test1 = llJsonGetValue(example, ["parent", 2]); | ||
string test2 = llJsonGetValue(example,["parent",1,"child that does not exist","etc."]); | string test2 = llJsonGetValue(example, ["parent", 1, "child that does not exist", "etc."]); | ||
</ | </syntaxhighlight> | ||
test1 and test2 will also both be [[JSON_NULL]]. | {{code|test1}} and {{code|test2}} will also both be [[JSON_NULL]]. | ||
These types of scenarios may be useful in determining valid JSON paths for the setter: in all the scenarios where [[JSON_NULL]] is returned, the same set of specifiers is able to be successfully used for [[llJsonSetValue]]. However if [[JSON_INVALID]] is returned, there is no guarantee the setter won't return an error. | These types of scenarios may be useful in determining valid JSON paths for the setter: in all the scenarios where [[JSON_NULL]] is returned, the same set of specifiers is able to be successfully used for [[llJsonSetValue]]. However if [[JSON_INVALID]] is returned, there is no guarantee the setter won't return an error. | ||
</blockquote> | </blockquote> | ||
When I run this script: | When I run this script: | ||
< | <syntaxhighlight lang="lsl2"> | ||
default | default | ||
{ | { | ||
Line 132: | Line 132: | ||
{ | { | ||
string example = "{\"parent\":[ , , , , ]}"; | string example = "{\"parent\":[ , , , , ]}"; | ||
string test1 = llJsonGetValue(example,["parent",2]); | string test1 = llJsonGetValue(example, ["parent", 2]); | ||
string test2 = llJsonGetValue(example,["parent",1,"child that does not exist","etc."]); | string test2 = llJsonGetValue(example, ["parent", 1, "child that does not exist", "etc."]); | ||
llOwnerSay(llEscapeURL(test1)); | llOwnerSay(llEscapeURL(test1)); | ||
llOwnerSay(llEscapeURL(test2)); | llOwnerSay(llEscapeURL(test2)); | ||
example = "{\"parent\":,}"; | example = "{\"parent\":,}"; | ||
test1 = llJsonGetValue(example,["parent"]); | test1 = llJsonGetValue(example, ["parent"]); | ||
test2 = llJsonGetValue(example,[" | test2 = llJsonGetValue(example, ["parente that doesn't exist", "etc."]); | ||
llOwnerSay(llEscapeURL(test1)); | llOwnerSay(llEscapeURL(test1)); | ||
llOwnerSay(llEscapeURL(test2)); | llOwnerSay(llEscapeURL(test2)); | ||
} | } | ||
} | } | ||
</ | </syntaxhighlight> | ||
I get: | I get: | ||
[07:58:42] Object: %EF%B7%90 | [07:58:42] Object: %EF%B7%90 | ||
Line 150: | Line 150: | ||
[07:58:42] Object: %EF%B7%90 | [07:58:42] Object: %EF%B7%90 | ||
that is, JSON_INVALID four times, I wonder if it's a change of mind or a bug. However, given the attention (or lack thereof) I've received in my bug report in | that is, [[JSON_INVALID]] four times, I wonder if it's a change of mind or a bug. However, given the attention (or lack thereof) I've received in my bug report in {{jira|BUG-6495}} I'm unsure if reporting it as a bug is a good idea. Maybe that section should just be removed? --[[User:Sei Lisa|Sei Lisa]] 08:07, 2 July 2014 (PDT) | ||
== Performance issues == | == Performance issues == | ||
I did some performance testing, comparing the use of string, list, and | I did some performance testing, comparing the use of string, list, and JSON functions. Detailed results are on my page (click my name below), but the summary is: | ||
* List searching and extraction is MUCH faster than llJsonGetValue (well over an order of magnitude) | * List searching and extraction is MUCH faster than [[llJsonGetValue]] (well over an order of magnitude) | ||
* Creating JSON strings by hand or through string concatenation is FAR more efficient than using llList2Json. | * Creating JSON strings by hand or through string concatenation is FAR more efficient than using llList2Json. | ||
* For extracting multiple values from a JSON object, it's substantially faster (but probably not an order of magnitude faster) to use llJson2List once, then extract data from the list. | * For extracting multiple values from a JSON object, it's substantially faster (but probably not an order of magnitude faster) to use [[llJson2List]] once, then extract data from the list. | ||
* The LSL string manipulation functions (llSubStringIndex, llGetSubString, llDeleteSubString, llInsertString) are individually slower than JSON functions, but for performing any full operation on a JSON string (i.e. search for an old value, replace it with a new one), the JSON functions almost certainly are more efficient. | * The LSL string manipulation functions ([[llSubStringIndex]], [[llGetSubString]], [[llDeleteSubString]], [[llInsertString]]) are individually slower than JSON functions, but for performing any full operation on a JSON string (i.e. search for an old value, replace it with a new one), the JSON functions almost certainly are more efficient. | ||
* Compared with using lists, using JSON strings is a lot slower. However, it may be more efficient in memory usage, and certainly can make for much more readable code and data, and this may be sufficient to offset the performance penalty. | * Compared with using lists, using JSON strings is a lot slower. However, it may be more efficient in memory usage, and certainly can make for much more readable code and data, and this may be sufficient to offset the performance penalty. | ||
* When using list functions, let LSL convert a string to a one-element list automatically (<code>list2 += "a"</code> beats <code>list2 += ["a"]</code>) | * When using list functions, let LSL convert a string to a one-element list automatically (<code>list2 += "a"</code> beats <code>list2 += ["a"]</code>) | ||
* llList2Integer(...) is faster than (integer)llList2String(...) | * {{code|[[llList2Integer]](...)}} is faster than {{code|(integer)[[llList2String]](...)}} | ||
I hope the new virtual world LL recently announced it's working on will have a scripting language that supports real object datatypes... hope hope... | I hope the new virtual world LL recently announced it's working on will have a scripting language that supports real object datatypes... hope hope... | ||
[[User:Brattle Resident|Brattle Resident]] 06:09, 11 July 2014 (PDT) | [[User:Brattle Resident|Brattle Resident]] 06:09, 11 July 2014 (PDT) |
Revision as of 12:26, 8 August 2023
I am concerned because the JSON format specifies at json.org that you can use escape codes like \u0000
to represent Unicode byte values in strings. But LSL has the ugly habit of censoring and altering strings so that a character with byte value of 0x0000
is removed from the string, and some functions like llSHA1String are essentially broken since they also convert UTF-16 integers between \u0128–\u0255
into UTF-8 byte values starting with %c2
(which therefore have a different integer value due to the addition of the extraneous byte). Is LSL also going to mangle JSON strings' byte values when it renders them into LSL strings? Won't this corrupt attempts at efficiently verifying the signatures of any incoming messages, and thwart attempts to generate proper signatures for some outgoing JSON-formatted requests? Or do the Lindens have plans to finally give us a proper suite of escape codes in LSL (or some other solution)?
--Gistya Eusebio 09:41, 30 May 2013 (PDT)
Add to that the complete crash-and-burn if your string starts with a quote, generating invalid JSON. I really don't get why LL finds it advantageous to include magic switches which forces everybody to do workaround for normal use, and ensures that all future JSON implementations in SL must be hand-coded to keep backwards compatibility with the spec breaks currently implemented. Tali Rosca 15:50, 19 June 2013 (PDT)
- I've been kicking and screaming about getting a robust JSON handling :-) 2594 got us some way, but we still have BUG-2736, which I consider so exceedingly ill-advised as to be a bug, despite the insistence that it's a really awesome feature. Tali Rosca 16:39, 21 June 2013 (PDT)
- The trailing quotation mark is the real nasty problem, not the leading one :D But yeah.. if they do add the capability to handle enquoted* text, how would that break anyone's scripts? Is anyone really relying on enquotation to invalidate their JSON strings on purpose? Besides when has breaking people's scripts stopped them from doing anything? I have tens of thousands of L$ worth of vehicles that are now worthless due to the Havok 4 update, but hey, life goes on. My copy of Microsoft Word 1.0 for Mac won't run on Mountain Lion either. I like backwards compatibility but we developers would be out of a job if software never had to be rewritten to work with the ever-evolving platforms that are out there :D
--Gistya Eusebio 07:58, 23 June 2013 (PDT)
* I know "enquoted" is not in the dictionary. However it is in the lexicon. :D- A (somewhat kludgy) workaround for the mishandling of escaped characters can now be found here. Hope someone finds this useful.LepreKhaun Resident 23:41, 29 August 2013 (PDT)
- The trailing quotation mark is the real nasty problem, not the leading one :D But yeah.. if they do add the capability to handle enquoted* text, how would that break anyone's scripts? Is anyone really relying on enquotation to invalidate their JSON strings on purpose? Besides when has breaking people's scripts stopped them from doing anything? I have tens of thousands of L$ worth of vehicles that are now worthless due to the Havok 4 update, but hey, life goes on. My copy of Microsoft Word 1.0 for Mac won't run on Mountain Lion either. I like backwards compatibility but we developers would be out of a job if software never had to be rewritten to work with the ever-evolving platforms that are out there :D
I feel the paragraph within "Specifying Json Elements" that begins with "When JSON is presented in human-readable form,..." should be rewritten to show that the JSON string in its entirety is actually the root node of the structure, signified by an empty list used as "specifiers". LepreKhaun Resident 15:23, 6 July 2013 (PDT)
Unsure of major edit
After making the edit on 16:04, 15 August 2013 to correct code examples that wouldn't compile, I've come to realize that the given examples, as well as the surrounding text, is in error since JSON disallows empty values, as one might find in a "sparse" array.
The author is pointing out "a rare exception" but then uses "{\"parent\":,}"
and "{\"parent\":[ , , , , ]}"
to illustrate. Neither of those can be arrived at with llList2Json(), since LSL doesn't allow empty list elements, but can only be obtained by hand coding to arrive at the non-compliant JSON strings.
I feel that whole section should be rewritten to simply point out to the reader that they are advised to use llList2Json() in the formation of JSON strings and avoid hand coding, which may well result in mal-formed constructions that could lead to confusing results in later operations.
However, doing so would excise a number of paragraphs and I'm unsure how that would set with the original author and others. Guidance on this would be appreciated. LepreKhaun Resident 05:25, 18 August 2013 (PDT)
JSON example
I want to submit a simple, working example on LSL JSON, but I don't know the right place for it
- I hope Strife will and can place it, here it is:
// JSON array forum example by Dora Gustafson, Studio Dora 2013
// Building a 3 by 5 array in a JSON Object
// Rows are indexed by name and columns are indexed by number = 0,1,2,3,4
string JSONVotes;
tellVotes(string voter)
{
string Js = llJsonGetValue(JSONVotes, [voter]);
list Jl = llParseString2List(Js, [",", "[", "]", "\""], []);
string output = llDumpList2String(Jl, ", ");
llOwnerSay("Votes from " + voter + " are: "+output);
}
default
{
state_entry()
{ // Building the JSON object
string votes = llList2Json(JSON_ARRAY, [0, 0, 0, 0, 0]); // one row
JSONVotes = llList2Json(JSON_OBJECT, ["Betty", votes, "Jerry", votes, "Pierre", votes]); // complete object
}
touch_end( integer num)
{ // Testing the JSON object
tellVotes("Betty");
tellVotes("Jerry");
tellVotes("Pierre");
// saving some random votes
JSONVotes = llJsonSetValue( JSONVotes, ["Betty", 1], (string)llFrand(100.0));
JSONVotes = llJsonSetValue( JSONVotes, ["Jerry", 4], (string)llFrand(100.0));
// testing
tellVotes("Betty");
tellVotes("Jerry");
tellVotes("Pierre");
// getting one vote, example
string s = llJsonGetValue(JSONVotes, ["Betty", 1]);
llOwnerSay("Betty votes " + s + " in second column");
}
}
Dora Gustafson 06:23, 25 August 2013 (PDT)
Inconsistent behavior on types
The documentation says: if the specifier list element is an integer, then the corresponding element in the JSON value must be an array and the list element is used as a zero-based index into the JSON array.
However, placing integers in a specifier used with a JSON_OBJECT and llJsonGetValue, they are automatically converted to strings. llList2Json and llJsonSetValue do not automatically convert integers to strings. Both will result in a JSON_INVALID if you try to put integers on the key side of a JSON_OBJECT.
This example code, according to the documentation shouldn't even work (I'd presume it's supposed to return a JSON_INVALID):
string test = llList2Json(JSON_OBJECT, ["1", "one", "2", "two", "3", "three"]);
llOwnerSay(llJsonGetValue(test, [3]));
This little sniplet produces "three". Additionally, using llJsonValueType in place of llJsonGetValue in the above example will result in JSON_STRING. Attempting something like 'llJsonSetValue(test, [3], "three");'
results in JSON_INVALID.
Chetar Ruby 09:15, 14 October 2013 (PDT)
- Yes, that's because what you meant to say was
llJsonSetValue(test, ["3"], "three")
– objects in LSL JSON land are always keyed on STRINGS– you can't pass numbers even when the string looks like a number. -- Winter Seale 12:35, 23 January 2014 (PST)- Right, but you're overlooking what was being pointed out, a string isn't being required using llJsonGetValue() on a JSON object. It's an anomaly, though I can't see how this might adversely affect anything. LepreKhaun Resident 14:29, 24 January 2014 (PST)
Major change to the last part
I've made a major change per BUG-6280; the change is http://wiki.secondlife.com/w/index.php?title=Json_usage_in_LSL&diff=1191136&oldid=1188019 - can someone please review it for accuracy? --Sei Lisa 18:09, 6 June 2014 (PDT)
- Review is not the same as undo. I expected some commentary on my edit, not a full reversal. Why is there an example using undocumented features, and why has the explanatory text that goes with the example been removed? --Sei Lisa 11:21, 9 June 2014 (PDT)
- I'm reverting Miranda's changes for several reasons.
- 1) The justification for the removal of the paragraph is redundancy. Nowhere in the article explains how the strings are interpreted when they aren't quoted inside the string itself (like the LSL string
"\"true\""
). The text merely says: "LSL strings which both begin and end with "\"" are interpreted literally as JSON strings, while those without are parsed when converted into JSON." But it does not explain what it means by parsed. It's very ambiguous, and it's not clear at all from that how it acts. The addition of a text that explains how the strings "true", "false" and "null" behave is therefore NOT redundant. - 2) There's no example of how to properly encode strings. Having an example that omits the quotes around the string values may lead to think that that's the norm, when that is undocumented behavior (nowhere in the article says what llList2Json or llJsonSetValue actually do with strings that don't decode to any valid JSON type - they happen to be translated to JSON strings, but some of these strings like "true", "false", "[]", etc. will not, and that's not exaplained).
- 3) The paragraph was added in this edit: http://wiki.secondlife.com/w/index.php?title=Json_usage_in_LSL&diff=1179766&oldid=1179765 - note how the example, initially written by Maestro, was buggy to the point that it used TRUE instead of JSON_TRUE, therefore the explanatory text was not redundant. It was merely not totally accurate, and I believe my edits made it clearer.
- --Sei Lisa 11:58, 9 June 2014 (PDT)
Text incorrect
This section looks factually incorrect:
If, anywhere along the line, any of the specified nodes do not exist, llJsonGetValue and llJsonValueType will almost always return JSON_INVALID. However there is a rare exception: if any specifier in the list leads to a node that has no characters in its JSON string value, JSON_NULL will be returned instead of JSON_INVALID — no matter what might come after it in the specifiers list. For example in:
string example = "{\"parent\":,}"; string test1 = llJsonGetValue(example, ["parent"]); string test2 = llJsonGetValue(example, ["parent that doesn't exist", "etc."]);
test1
andtest2
will both be JSON_NULL.The same is true for arrays: a specifier pointing to any element of a valid array will return JSON_NULL anywhere there is no JSON value specified at given position in the array:
string example = "{\"parent\":[ , , , , ]}"; string test1 = llJsonGetValue(example, ["parent", 2]); string test2 = llJsonGetValue(example, ["parent", 1, "child that does not exist", "etc."]);
test1
andtest2
will also both be JSON_NULL.These types of scenarios may be useful in determining valid JSON paths for the setter: in all the scenarios where JSON_NULL is returned, the same set of specifiers is able to be successfully used for llJsonSetValue. However if JSON_INVALID is returned, there is no guarantee the setter won't return an error.
When I run this script:
default
{
state_entry()
{
string example = "{\"parent\":[ , , , , ]}";
string test1 = llJsonGetValue(example, ["parent", 2]);
string test2 = llJsonGetValue(example, ["parent", 1, "child that does not exist", "etc."]);
llOwnerSay(llEscapeURL(test1));
llOwnerSay(llEscapeURL(test2));
example = "{\"parent\":,}";
test1 = llJsonGetValue(example, ["parent"]);
test2 = llJsonGetValue(example, ["parente that doesn't exist", "etc."]);
llOwnerSay(llEscapeURL(test1));
llOwnerSay(llEscapeURL(test2));
}
}
I get:
[07:58:42] Object: %EF%B7%90 [07:58:42] Object: %EF%B7%90 [07:58:42] Object: %EF%B7%90 [07:58:42] Object: %EF%B7%90
that is, JSON_INVALID four times, I wonder if it's a change of mind or a bug. However, given the attention (or lack thereof) I've received in my bug report in BUG-6495 I'm unsure if reporting it as a bug is a good idea. Maybe that section should just be removed? --Sei Lisa 08:07, 2 July 2014 (PDT)
Performance issues
I did some performance testing, comparing the use of string, list, and JSON functions. Detailed results are on my page (click my name below), but the summary is:
- List searching and extraction is MUCH faster than llJsonGetValue (well over an order of magnitude)
- Creating JSON strings by hand or through string concatenation is FAR more efficient than using llList2Json.
- For extracting multiple values from a JSON object, it's substantially faster (but probably not an order of magnitude faster) to use llJson2List once, then extract data from the list.
- The LSL string manipulation functions (llSubStringIndex, llGetSubString, llDeleteSubString, llInsertString) are individually slower than JSON functions, but for performing any full operation on a JSON string (i.e. search for an old value, replace it with a new one), the JSON functions almost certainly are more efficient.
- Compared with using lists, using JSON strings is a lot slower. However, it may be more efficient in memory usage, and certainly can make for much more readable code and data, and this may be sufficient to offset the performance penalty.
- When using list functions, let LSL convert a string to a one-element list automatically (
list2 += "a"
beatslist2 += ["a"]
) [[llList2Integer]](...)
is faster than(integer)[[llList2String]](...)
I hope the new virtual world LL recently announced it's working on will have a scripting language that supports real object datatypes... hope hope... Brattle Resident 06:09, 11 July 2014 (PDT)