User:LepreKhaun Resident/Json Get Value Safe

From Second Life Wiki
Jump to: navigation, search

[NOTE: Pages within my Name Space are a WIP and constantly changing. As my understanding of the problems I attempt to address and the grasp of the subject matter itself deepens, I regularly review what I have written and update the content as better algorithms occur to me.

However, for this process of refinement, improvement and tweaking to result in something that might (hopefully!) benefit the community at large, I ask that comments, suggested improvements, corrections of fact or your own personal style preferences be made ONLY on the Discussion Pages within my Name Space. Thank you!]

//////////////////////////////
// function string uJsonGetValueSafe (string jsonText, list specifiers)
// Same specification as llJsonGetValue() except returns all Strings
// enclosed within double quotes and exactly as they appear within jsonText
//
// Version 1.0 by LepreKhaun 9/9/20
// May be freely used, modified and distributed with this header intact.
// Compiled Size = 4,608 bytes
///////////////////////////////
 
string uJsonGetValueSafe (string jsonText, list specifiers)
{
	string rString = "";
 
	// no need for further processing if not String
	if (llJsonValueType(jsonText, specifiers) != JSON_STRING) 
	{
		rString = llJsonGetValue(jsonText, specifiers);
 
	// specifiers may be an empty list and we're dealing with simply a JSON_STRING
	}
	else if (llGetListLength(specifiers) == 0) 
	{
		rString = jsonText;
 
	}
	else
	{
		// used for assembling the return string
		string char = "";
		string prevChar = "";
 
		// boolean flag
		integer inQuote = FALSE;
 
		// used to step through Array/Object Values
		// NOTE: Takes valid, compliant Json text for granted
		integer inArrayObject = 0;
 
		integer iter = 0;
 
		// we need to extract the parent of the String Value
		list valueSpecifier = llList2List(specifiers, -1, -1);
 
		// shorten specifiers to point to parent of the Value
		specifiers = llDeleteSubList(specifiers, -1, -1);
 
		// extract the parent
		jsonText = llJsonGetValue(jsonText, specifiers);
 
		if (llGetListEntryType(valueSpecifier, 0) == TYPE_INTEGER)
		{
			// we're dealing with an array
			integer sPos = llList2Integer(valueSpecifier, 0);
 
			// used to step through array Values
			integer count = 0;
 
			// find start of our String Value within array
			while (count < sPos)
			{
				char = llGetSubString(jsonText, ++iter, iter);
				if (char == "\"" && prevChar != "\\") //"//
				{
					inQuote = !inQuote;
				}
				else if ((char == "[" || char == "{") && !inQuote)
				{
					++inArrayObject;
				}
				else if ((char == "]" || char == "}") && !inQuote)
				{
					--inArrayObject;
				}
				else if (char == "," && !inQuote && !inArrayObject)
				{
					++count;
				}
				prevChar = char;
			}
			// eat possible white space
			while ((rString = llGetSubString(jsonText, ++iter, iter)) != "\""){};
			// Now assemble return string
			inQuote = TRUE;
			prevChar = "";
			while (inQuote)
			{
				char = llGetSubString(jsonText, ++iter, iter);
				rString = rString + char;
				if (char == "\"" && prevChar != "\\") //"//
				{
					inQuote = FALSE;
				}
				prevChar = char;
			}
 
		}
		else
		{
			// otherwise, we must be dealing with an object
 
			// make the key for comparison
			string sKey = llList2String(valueSpecifier, 0);
 
			// and encode it as a JSON_STRING if it's not
			if (llGetSubString(sKey, 0, 0) != "\"")
			{
				sKey = "\"" + sKey + "\"";
			}
 
			// used for a possible "Key" used in comparison
			string pKey;
 
			integer jtLength = llStringLength(jsonText) - 1;
 
			while (iter < jtLength)
			{
				// go to start of a possible "Key"
				while (llGetSubString(jsonText, ++iter, iter) != "\"")
				{
				};
 
				// form pKey
				pKey = "\"";
				inQuote = TRUE;
				while (inQuote)
				{
					char = llGetSubString(jsonText, ++iter, iter);
					pKey = pKey + char;
					if (char == "\"" && prevChar != "\\") //"//
					{
						inQuote = FALSE;
					}
					prevChar = char;
				}
 
				// move to start of Value, eating possible white space
				while (llGetSubString(jsonText, ++iter, iter) != ":")
				{
				};
 
				char = llEscapeURL(llGetSubString(jsonText, ++iter, iter));
 
				while (char == "%20" || char == "%09" || char == "%0a" || char == "%0d")
				{
					char = llEscapeURL(llGetSubString(jsonText, ++iter, iter));
				}
 
				char = llUnescapeURL(char);
 
				// check "Key" for match with sKey AND start of a String Value
				if (pKey == sKey && char == "\"")
				{
					// assemble Value
					prevChar = "";
					inQuote = TRUE;
					rString = "\"";
 
					while (inQuote)
					{
						char = llGetSubString(jsonText, ++iter, iter);
						rString = rString + char;
						if (char == "\"" && prevChar != "\\") //"//
						{
							inQuote = FALSE;
						}
						prevChar = char;
					}
 
					// move to ',' or '}' (next "Key"Value pair or end of object)
					while(!(llGetSubString(jsonText, ++iter, iter) == "," || llGetSubString(jsonText, iter, iter) == "}")){};
				}
				else
				{
					// eat Value to next Key or end of object
					integer inValue = TRUE;
					prevChar = "";
					inArrayObject = 0;
					inQuote = FALSE;
 
					while (inValue)
					{
						char = llGetSubString(jsonText, iter, iter++);
						if (char == "\"" && prevChar != "\\") //"//
						{
							inQuote = !inQuote; 
						}
						else if ((char == "," || char == "}") && !inQuote && !inArrayObject)
						{
							inValue = FALSE;
							--iter;
						}
						else if ((char == "[" || char == "{") && !inQuote)
						{
							++inArrayObject;
						}
						else if ((char == "]" || char == "}") && !inQuote)
						{
							--inArrayObject;
						}
						prevChar = char;
					}
				}
			}
		}
	}
	return rString;
}
 
// example usage and comparison
default {
	touch_end (integer i){
		string jText = "{\"B\":\"Z\",\"C\":[1,\"a\\r\\t\\f\",3],\"A\":\"H\\n\\u23AF\"}";
		llOwnerSay(jText); // => '{"B":"Z","C":[1,"a\r\t\f",3],"A":"H\n\u23AF"}'
 
		llOwnerSay(llJsonGetValue(jText, ["A"])); // => 'H' then Unicode character for New Line then 'u23AF'
		llOwnerSay(uJsonGetValueSafe(jText, ["A"])); // => "H\n\u23AF"
 
		llOwnerSay(llJsonGetValue(jText, ["B"])); // => 'Z'
		llOwnerSay(uJsonGetValueSafe(jText, ["B"])); // => "Z"
 
		llOwnerSay(llJsonGetValue(jText, ["C"])); // => '[1,"a\r\t\f",3]'
		llOwnerSay(uJsonGetValueSafe(jText, ["C"])); // => '[1,"a\r\t\f",3]'
 
		llOwnerSay(llJsonGetValue(jText, ["C", 0])); // => '1'
		llOwnerSay(uJsonGetValueSafe(jText, ["C", 0])); // => '1'
 
		llOwnerSay(llJsonGetValue(jText, ["C", 1])); // => 'a' then Unicode characters for the escaped sequences
		llOwnerSay(uJsonGetValueSafe(jText, ["C", 1])); // => "a\r\t\f"
 
		llOwnerSay(llJsonGetValue(jText, ["C", 2])); // => '3'
		llOwnerSay(uJsonGetValueSafe(jText, ["C", 2])); // => '3'
	}
}

== More Json Tips, Tricks and Coding Examples ==