User:Vegas Silverweb/LSL to JSON

From Second Life Wiki
Jump to: navigation, search

JSON's a pleasant way to serialize object representations. At this point basically every general-purpose programming language with some form of object abstraction supports JSON. It's particularly handy in say PHP, where web-to-object is just a json_decode() away.

PHP certainly doesn't speak LSL strided lists, and LSL's list dumping and CSV implementations are rather unique to LSL. With a little trickery, though, you can make use of the fact that the key type in LSL is just a string with delusions of grandeur and construct JSON representations of objects with arbitrary complexity...with some caveats.

Note a few optimizations and shortcuts are omitted for the sake of keeping things a little more readable/understandable.

First let's turn a strided list into a JSON object:

string vsList2JSON(list input)
	list labels;
	if(llGetListLength(input) % 2 != 0)
		llSay(DEBUG_CHANNEL,"vsList2JSON must be a 2-strided list!");
		return "{}";
	string sTemp = "{";
	integer i;
	for(i = 0; i < llGetListLength(input); i += 2)
		string label = llList2String(input,i);
		sTemp += "\""+label+"\":";
		integer entryType = llGetListEntryType(input,i+1);
		if(entryType>2 && entryType!=4)
		else if(entryType==2)
		else if(entryType==1)
		else if(entryType==4)
		if(llGetListLength(input)>i+2) sTemp=sTemp+",";
	sTemp += "}";
	return sTemp;

It's a little ugly, but for most cases works fine. The part that needs a little explaining is how keys are handled (type 4). We want to treat it as a string, but not surround it with quotes. This'll come in handy later...

So we have a strided list in object format. We might need a JSON array, too:

<lsl> string vsList2JSONArray(list input) { string sTemp = "["; integer i; for(i=0;i<llGetListLength(input);i++) { integer entryType = llGetListEntryType(input,i); if(entryType==3)

   sTemp=sTemp+"\""+llList2String(input,i)+"\""; else
   sTemp=sTemp+(string)llList2Float(input,i); else
   sTemp=sTemp+(string)llList2String(input,i); else
   if(llGetListLength(input)>i+1) sTemp=sTemp+",";

} sTemp += "]"; return sTemp; } </lsl>

And there we go, this will wrap and delimit a list so in most cases a JSON parser will treat the list dump as valid JSON. So, let's put them together and make a JSON object of moderate complexity:

<lsl> default {

   state_entry() {
       list values = ["Red","Green","Blue"];

} </lsl>

And the output we get: {"beer":{"Hey":"You","Out":"There"},"number":34,"floater":1.232000,"EmbeddedArray":["Red","Green","Blue"]}

Pop that into your favorite JSON decoder or JSON validator, nice and valid!

Basically all you have to do is preface inner calls of the *2JSON functions with a cast to (key) and the *JSON functions will not wrap the string in quotes, allowing you to construct JSON representations of arbitrary complexity.

Which brings us to the cavaets. JSON's a little finicky, and a number of the characters that could find their way into strings will make fragile JSON parsers, including Javascript interpreters, choke. If there's any chance of forward slashes, backslashes, double quotes, or Unicode characters in your JSON, it's probably wise to let llStringToBase64 wrap the string in a nice, safe encoding. Basically any language that handles JSON probably has facilities to decode base 64 for you as well.

At this point you might be excited about the prospect of serving Javascript objects from HTTP-in scripts, doing some fun stuff with callbacks, and so on. You shouldn't be. The market share leader, IE, refuses to interpret Javascript served with a text/plain content type. Most other browsers are kind enough to do so - including the SL "Media Browser," and 2.0's "Shared Media." If you're want to get mad at Microsoft (about this), don't...they're actually trying to follow standards by expecting application/javascript or application/ecmascript, and allowing text/html fallback for the benefit of nonstandard HTTP servers.

Nevertheless handy particularly for things like Shared Media(tm) (heh) hacks.