#ifndef UDO_JSON #define UDO_JSON ## /* JSON formatting and parsing This file is part of the SONICS UDO collection by Richard Knight 2022 License: GPL-2.0-or-later http://1bpm.net */ #include "/string_tools.udo" #include "/array_tools.udo" /* Create a new empty JSON string Sjson json_init Sout empty JSON object/string */ opcode json_init, S, 0 xout "{}" endop /* Append raw string to JSON string Sout json_append Sjson, StoAppend Sout string with appendage Sjson input string to append to StoAppend string to append */ opcode json_append, S, SS Sjson, StoAppend xin ilen strlen Sjson Sjson = (ilen < 2) ? "{" : strsub(Sjson, 0, ilen-1) Sout = sprintf("%s%s%s}", Sjson, (strlen(Sjson) == 1) ? "" : ",", StoAppend) xout Sout endop /* Append JSON object string to JSON object string with key Sout json_appendobject Sjson, Skey, Svalue Sout string with appendage Sjson input string to append to Skey key for appending Svalue value for appending */ opcode json_appendobject, S, SSS Sjson, Skey, Svalue xin Sout = json_append(Sjson, sprintf("\"%s\":%s", Skey, Svalue)) xout Sout endop /* Append string to JSON object string with key Sout json_appendstring Sjson, Skey, Svalue Sout string with quoted appendage Sjson input string to append to Skey key for appending Svalue value for appending */ opcode json_appendstring, S, SSS Sjson, Skey, Svalue xin Sout = json_append(Sjson, sprintf("\"%s\":\"%s\"", Skey, Svalue)) xout Sout endop /* Append numeric value to JSON object string with key Sout json_appendvalue Sjson, Skey, ivalue Sout string with appendage Sjson input string to append to Skey key for appending ivalue value for appending */ opcode json_appendvalue, S, SSi Sjson, Skey, ivalue xin Sformat = (frac(ivalue) == 0) ? "\"%s\":%d" : "\"%s\":%f" Sout = json_append(Sjson, sprintf(Sformat, Skey, ivalue)) xout Sout endop /* Append numeric array to JSON object string with key Sout json_appendarray Sjson, Skey, iarray[] Sout string with appendage Sjson input string to append to Skey key for appending iarray[] array for appending */ opcode json_appendarray, S, SSi[]o Sjson, Skey, iarray[] xin Sformatted = "" ilen = lenarray(iarray) index = 0 while (index < ilen) do ivalue = iarray[index] Sformat = (frac(ivalue) == 0) ? "%d" : "%f" Sformatted strcat Sformatted, sprintf(Sformat, ivalue) if (index != ilen - 1) then Sformatted strcat Sformatted, "," endif index += 1 od Sout = json_appendobject(Sjson, Skey, sprintf("[%s]", Sformatted)) xout Sout endop /* Append string array to JSON object string with key Sout json_appendarray Sjson, Skey, Sarray[] Sout string with appendage Sjson input string to append to Skey key for appending Sarray[] array for appending iomitempty leave out empty strings */ opcode json_appendarray, S, SSS[]o Sjson, Skey, Sarray[], iomitempty xin Sformatted = "" ilen = lenarray(Sarray) index = 0 while (index < ilen) do isempty = (strcmp(Sarray[index], "") == 0) ? 1 : 0 if ((iomitempty == 0 && isempty == 1) || isempty == 0) then Sformatted strcat Sformatted, sprintf("\"%s\",", Sarray[index]) endif index += 1 od Sout = json_appendobject(Sjson, Skey, sprintf("[%s]", strsub(Sformatted, 0, strlen(Sformatted) - 1))) xout Sout endop /* Append f-table to JSON object string with key Sout json_appendtable Sjson, Skey, ifn Sout string with appendage Sjson input string to append to Skey key for appending ifn f-table number */ opcode json_appendtable, S, SSi Sjson, Skey, ifn xin Sformatted = "" ilen = ftlen(ifn) index = 0 while (index < ilen) do ivalue = table:i(index, ifn) Sformat = (frac(ivalue) == 0) ? "%d" : "%f" Sformatted strcat Sformatted, sprintf(Sformat, ivalue) if (index != ilen - 1) then Sformatted strcat Sformatted, "," endif index += 1 od Sout = json_appendobject(Sjson, Skey, sprintf("[%s]", Sformatted)) xout Sout endop /* Obtain an object, array or value from a json object given a string key itype, Stringvalue, inumericvalue json_parse Sjson, Skey itype type of returned value: 0=string, 1=object, 2=array, 3=numeric (1 and 2 are returned as strings for further parsing) Stringvalue the requested value as a string inumericvalue the numeric value if itype is 3 , otherwise -1 Sjson the json to parse Skey the key to lookup */ opcode json_parse, iSi, SS Sjson, Skey xin itype = -1 index = 0 idepth = 0 idepthrequest = -1 iobjectstart = -1 inval = 0 instring = 0 inrequested = 0 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, "[") == 0 && instring == 0) then idepth += 1 if (inrequested == 1 && iobjectstart == -1) then iobjectstart = index endif elseif (strcmp(Schar, "]") == 0 && instring == 0) then idepth -= 1 if (idepthrequest == idepth) then itype = 2 goto complete endif elseif (strcmp(Schar, "{") == 0 && instring == 0) then idepth += 1 if (inrequested == 1 && iobjectstart == -1) then iobjectstart = index endif elseif (strcmp(Schar, "}") == 0 && instring == 0) then idepth -= 1 if (idepthrequest == idepth) then itype = 1 goto complete endif elseif (strcmp(Schar, ":") == 0 && instring == 0) then inval = 1 iobjectstart = index + 1 elseif (strcmp(Schar, ",") == 0 && instring == 0) then if (inval == 1) then inval = 0 if (inrequested == 1 && idepthrequest == idepth) then index -= 1 itype = 3 goto complete endif endif elseif (strcmp(Schar, "\"") == 0) then instring = 1 - instring if (instring == 1) then istringstart = index + 1 else String = strsub(Sjson, istringstart, index) if (inrequested == 1 && idepthrequest == idepth) then iobjectstart = istringstart index -= 1 itype = 0 goto complete elseif (strcmp(String, Skey) == 0) then idepthrequest = idepth inrequested = 1 endif endif endif index += 1 od complete: Stringvalue = strsub(Sjson, iobjectstart, index+1) ; +1 required? if (itype == 3) then ivalid, inumericvalue try_strtod strstrip(Stringvalue) itype = (ivalid == 1) ? 3 : 0 endif xout itype, Stringvalue, inumericvalue endop ; itype: 0=S, 1=i opcode _json_getarray, S[]i[], Si Sjson, itype xin index = 0 instring = 0 iobjectstart = -1 iobjectcount = 0 iwriteindex = 0 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, ",") == 0 && instring == 0) then iobjectcount += 1 elseif (strcmp(Schar, "\"") == 0) then instring = 1 - instring endif index += 1 od if (itype == 0) then Soutput[] init iobjectcount + 1 ioutput[] init 1 else Soutput[] init 1 ioutput[] init iobjectcount + 1 endif index = 0 instring = 0 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, ",") == 0 && instring == 0) then if (itype == 1) then ioutput[iwriteindex] = strtod(strsub(Sjson, iobjectstart, index)) iobjectstart = index + 1 endif iwriteindex += 1 elseif (strcmp(Schar, "[") == 0 && instring == 0) then iobjectstart = index + 1 elseif (strcmp(Schar, "]") == 0 && instring == 0) then if (itype == 1) then ioutput[iwriteindex] = strtod(strsub(Sjson, iobjectstart, index)) endif elseif (strcmp(Schar, "\"") == 0) then if (instring == 0) then iobjectstart = index + 1 elseif (itype == 0) then Svalue = strsub(Sjson, iobjectstart, index) Soutput[iwriteindex] = Svalue endif instring = 1 - instring endif index += 1 od xout Soutput, ioutput endop opcode json_getstringarray, S[], S Sjson xin Soutput[], i_[] _json_getarray Sjson, 0 xout Soutput endop opcode json_getnumericarray, i[], S Sjson xin S_[], ioutput[] _json_getarray Sjson, 1 xout ioutput endop /* Obtain an object, array or value from a json array given an index itype, Stringvalue, inumericvalue json_parsearray Sjson, irequestindex itype type of returned value: 0=string, 1=object, 2=array, 3=numeric (1 and 2 are returned as strings for further parsing) Stringvalue the requested value as a string inumericvalue the numeric value if itype is 3 , otherwise -1 Sjson the json to parse irequestindex index in the array to obtain */ opcode json_parsearray, iSi, Si Sjson, irequestindex xin itype = -1 inmainarray = 0 index = 0 iobjectstart = 0 iscanindex = 0 instring = 0 idepth = 0 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, "[") == 0 && instring == 0) then if (inmainarray == 0) then inmainarray = 1 iobjectstart = index + 1 else idepth += 1 endif elseif (strcmp(Schar, "]") == 0 && instring == 0) then if (inmainarray == 1 && idepth == 0) then itype = 0 goto complete else idepth -= 1 itype = 2 endif elseif (strcmp(Schar, "{") == 0 && inmainarray == 1 && instring == 0) then idepth += 1 elseif (strcmp(Schar, "}") == 0 && inmainarray == 1 && instring == 0) then idepth -= 1 itype = 1 elseif (strcmp(Schar, ",") == 0 && idepth == 0 && inmainarray == 1 && instring == 0) then if (iscanindex == irequestindex) then itype = 0 goto complete endif iobjectstart = index + 1 iscanindex += 1 elseif (strcmp(Schar, "\"") == 0 && idepth == 0 && inmainarray == 1 && iscanindex == irequestindex) then instring = 1 - instring if (instring == 1) then iobjectstart = index + 1 else itype = 0 goto complete endif endif index += 1 od complete: Stringvalue = strsub(Sjson, iobjectstart, index) if (itype == 0) then ivalid, inumericvalue try_strtod strstrip(Stringvalue) itype = (ivalid == 1) ? 3 : 0 endif xout itype, Stringvalue, inumericvalue endop /* Get the length of a json array ilength json_arraylength Sjson ilength length determined Sjson input array */ opcode json_arraylength, i, S Sjson xin idepth = -1 idepthmax = -1 ihasitems = 0 instring = 0 index = 0 itemnum = 0 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, "[") == 0 && instring == 0) then idepth += 1 idepthmax += 1 elseif (strcmp(Schar, "]") == 0 && instring == 0) then idepth -= 1 if (idepth < 0) then goto complete endif elseif (strcmp(Schar, "{") == 0 && instring == 0 && idepth >= 0) then idepth += 1 idepthmax += 1 elseif (strcmp(Schar, "}") == 0 && instring == 0 && idepth >= 0) then idepth -= 1 elseif (strcmp(Schar, ",") == 0 && instring == 0 && idepth == 0) then itemnum += 1 elseif (strcmp(Schar, "\"") == 0) then if (idepth == 0) then ihasitems = 1 endif instring = 1 - instring endif index += 1 od complete: if (idepthmax != 0 || ihasitems == 1) then itemnum += 1 endif xout itemnum endop /* Get the keys from an object Skeys[] json_getkeys Sjson Skeys[] the keys found Sjson input json */ opcode json_getkeys, S[], S Sjson xin Stemp[] init 999 itempindex init 0 idepth = -1 instring = 0 index = 0 inmainobject = 0 iskey = 1 while (index < strlen(Sjson)) do Schar = strsub(Sjson, index, index + 1) if (strcmp(Schar, "[") == 0 && instring == 0 && idepth >= 0) then idepth += 1 elseif (strcmp(Schar, "]") == 0 && instring == 0 && idepth >= 0) then idepth -= 1 elseif (strcmp(Schar, "{") == 0 && instring == 0) then idepth += 1 elseif (strcmp(Schar, "}") == 0 && instring == 0) then idepth -= 1 if (idepth < 0) then goto complete endif elseif (strcmp(Schar, ":") == 0 && instring == 0 && idepth == 0) then iskey = 0 elseif (strcmp(Schar, ",") == 0 && instring == 0 && idepth == 0) then iskey = 1 elseif (strcmp(Schar, "\"") == 0 && idepth == 0) then instring = 1 - instring if (instring == 1) then istringstart = index + 1 elseif (iskey == 1) then Stemp[itempindex] = strsub(Sjson, istringstart, index) itempindex += 1 endif endif index += 1 od complete: Skeys[] init itempindex ;+ 1 index = 0 while (index < itempindex) do Skeys[index] = Stemp[index] index += 1 od xout Skeys endop #end