├── LICENSE ├── README.md └── libJSON.axi /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Eric Walters 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netlinx-libJSON 2 | A Netlinx library for validating, parsing, modifying, and creating JSON data 3 | 4 | # Latest Updates 5 | - Changed JSON_ParseObject to JSON_ParseValidObject and removed the JSON_Validate call in that function 6 | - Added a new JSON_ParseObject that calls JSON_Validate and then calls JSON_ParseValidObject if it's valid 7 | - Added a new JSON_FindObjectInString function that runs a validation routine on an input string and returns the first valid JSON string it finds -------------------------------------------------------------------------------- /libJSON.axi: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME='libJSON v2_0' 2 | // --------------------------------------------------------------------------------------------------------------------- 3 | // LIBRARY: libJSON 4 | // AUTHOR: sentry07@gmail.com 5 | // PURPOSE: To provide functions to verify, parse, and build JSON encoded objects 6 | // 7 | // THIS LIBRARY RELIES ON THE FACT THAT FUNCTION PARAMETERS ARE BYREF, SO MOST DATA IS RETURNED THROUGH PARAMETERS 8 | // MAKE SURE YOU READ THE NOTES ON ALL THE FUNCTIONS BEFORE USING THEM 9 | // 10 | // --------------------------------------------------------------------------------------------------------------------- 11 | // LIBRARY NOTES: 12 | // The two main functions you will probably use from this library are JSON_ParseObject and JSON_ParseArray. 13 | // 14 | // JSON_ParseObject takes in the JSON string and the return object as parameters, and returns the number of KV pairs the 15 | // function parsed. JSON_ParseObject will call JSON_Validate before parsing to make sure it's a valid object, so calling 16 | // JSON_Validate on a string is not necessary unless you are checking for a valid object before calling JSON_Parse. 17 | // JSON_Validate returns compacted JSON with all extra whitespace removed. The resulting _JSON_Object has the K:V pair 18 | // count in it, as well as an array of Key:Value pair objects, which include the value type. Please see the structures 19 | // below. 20 | // 21 | // JSON_ParseArray takes a JSON array string and the return object as parameters, and returns the number of items in the 22 | // array parsed. 23 | // 24 | // --------------------------------------------------------------------------------------------------------------------- 25 | // 26 | // Valid JSON data is repesented in Key:Value pairs, where the Key is a string literal and Value can be one of: 27 | // * String literal (enclosed in double quotes) 28 | // * positive or negative integer or float (non-enclosed) 29 | // * an array of values (enclosed in square brackets) 30 | // * a nested JSON object (enclosed in squigly brackets) 31 | // * True/False (true or false, non-enclosed) 32 | // * Null (null, non-enclosed) 33 | // 34 | // --------------------------------------------------------------------------------------------------------------------- 35 | // Version Notes: 36 | // v2_0: A full rewrite from the ground up to make using JSON data easier and parse properly instead of the hacky 37 | // stuff I was doing before 38 | // v1_0: First version that was very hard to use and relied on all values to be strings 39 | // --------------------------------------------------------------------------------------------------------------------- 40 | // User Structures: 41 | // 42 | // STRUCTURE _JSON_Object 43 | // { 44 | // INTEGER Count // Number of Key:Value pairs in this object 45 | // _KVPair KV[_JSON_MaxPairs] // The actual Key:Value data 46 | // STRUCTURE _KVPair 47 | // { 48 | // CHAR K[_JSON_KeySize] // Key name 49 | // CHAR V[_JSON_ValueSize] // Value data 50 | // INTEGER nType // Value type 51 | // } 52 | // } 53 | // 54 | // STRUCTURE _JSON_Array 55 | // { 56 | // INTEGER Count // Number of values stored in array 57 | // _Value List[_JSON_ArraySize] // Value storage 58 | // STRUCTURE _Value 59 | // { 60 | // CHAR Value[_JSON_ValueSize] // Actual value 61 | // INTEGER nType // Value type 62 | // } 63 | // } 64 | 65 | // --------------------------------------------------------------------------------------------------------------------- 66 | // Functions: 67 | // DEFINE_FUNCTION CHAR[] _JSON_TrimWhiteSpace (CHAR cInput[]) 68 | // DEFINE_FUNCTION INTEGER JSON_FindObjectInString (CHAR cInput[],CHAR cJSON_String[]) 69 | // DEFINE_FUNCTION INTEGER JSON_ParseObject (CHAR cInput[],_JSON_Object jObject) 70 | // DEFINE_FUNCTION INTEGER JSON_Validate (CHAR cInput[],CHAR cCleaned[]) 71 | // DEFINE_FUNCTION INTEGER JSON_ParseValidObject (CHAR cInput[],_JSON_Object jObject) 72 | // DEFINE_FUNCTION INTEGER JSON_ParseArray (CHAR cInput[],_JSON_Array jReturn) 73 | // DEFINE_FUNCTION INTEGER JSON_HasKey (_JSON_Object jObject,CHAR cKeyName[]) 74 | // DEFINE_FUNCTION CHAR[] JSON_GetValue (_JSON_Object jObject,CHAR cKeyName[]) 75 | // DEFINE_FUNCTION SINTEGER JSON_SetValue (_JSON_Object jObject,CHAR cKeyName[],CHAR cValue[],INTEGER nType) 76 | // DEFINE_FUNCTION SINTEGER JSON_DeleteKey (_JSON_Object jObject,CHAR cKeyName[]) 77 | // DEFINE_FUNCTION INTEGER JSON_GetType (_JSON_Object jObject,CHAR cKeyName[]) 78 | // DEFINE_FUNCTION CHAR[] JSON_ObjectToString (_JSON_Object jObject) 79 | // DEFINE_FUNCTION CHAR[] JSON_ArrayToString (_JSON_Array jArray) 80 | // DEFINE_FUNCTION INTEGER JSON_ClearObject (_JSON_Object jObject) 81 | // DEFINE_FUNCTION CHAR[] NumArrayToJSON (SLONG nArray[],INTEGER nLength) 82 | // DEFINE_FUNCTION CHAR[] StringArrayToJSON (CHAR cArray[][],INTEGER nLength) 83 | // --------------------------------------------------------------------------------------------------------------------- 84 | 85 | // --------------------------------------------------------------------------------------------------------------------- 86 | // JSON Constants 87 | // --------------------------------------------------------------------------------------------------------------------- 88 | DEFINE_CONSTANT 89 | 90 | CHAR JSON_WhiteSpace[] = { $20, $0D, $0A, $09 } // Space literal, CR, LF, Tab 91 | 92 | // --------------------------------------------------------------------------------------------------------------------- 93 | // JSON Object size constants 94 | // --------------------------------------------------------------------------------------------------------------------- 95 | INTEGER _JSON_InputSize = 20000 // Size of buffer for JSON string 96 | INTEGER _JSON_MaxPairs = 50 // Max number of Key:Value pairs in the _JSON_Object Structure; adjust this if you get OutOfMemory error 97 | INTEGER _JSON_KeySize = 50 // String size for all Keys 98 | INTEGER _JSON_ValueSize = 2000 // String size for all Values 99 | INTEGER _JSON_ArraySize = 64 // Max number of values in _JSON_Array 100 | 101 | // --------------------------------------------------------------------------------------------------------------------- 102 | // Data type constants 103 | // --------------------------------------------------------------------------------------------------------------------- 104 | INTEGER _JSON_IsObject = 1 105 | INTEGER _JSON_IsString = 2 106 | INTEGER _JSON_IsNumber = 3 107 | INTEGER _JSON_IsArray = 4 108 | INTEGER _JSON_IsBool = 5 109 | INTEGER _JSON_IsNull = 6 110 | 111 | CHAR _JSON_Types[][10] = { 112 | 'Object', 113 | 'String', 114 | 'Number', 115 | 'Array', 116 | 'Bool', 117 | 'Null' 118 | } 119 | 120 | // --------------------------------------------------------------------------------------------------------------------- 121 | // Error/Status codes 122 | // --------------------------------------------------------------------------------------------------------------------- 123 | SINTEGER _JSON_Success = 0 124 | SINTEGER _JSON_KeyNotFound = -1 // GetValue, DeleteKey 125 | SINTEGER _JSON_OutOfMemory = -2 // SetValue 126 | SINTEGER _JSON_InvalidValue = -3 // SetValue 127 | 128 | // --------------------------------------------------------------------------------------------------------------------- 129 | // JSON Structures 130 | // --------------------------------------------------------------------------------------------------------------------- 131 | DEFINE_TYPE 132 | 133 | STRUCTURE _Value 134 | { 135 | CHAR Value[_JSON_ValueSize] // Actual value 136 | INTEGER nType // Value type 137 | } 138 | 139 | STRUCTURE _JSON_Array 140 | { 141 | INTEGER Count // Number of values stored in array 142 | _Value List[_JSON_ArraySize] // Value storage 143 | } 144 | 145 | // Basic Key:Value pair 146 | STRUCTURE _KVPair 147 | { 148 | CHAR K[_JSON_KeySize] // Key name 149 | CHAR V[_JSON_ValueSize] // Value data 150 | INTEGER nType // Value type 151 | } 152 | 153 | // The actual object that will be returned 154 | STRUCTURE _JSON_Object 155 | { 156 | INTEGER Count // Number of Key:Value pairs in this object 157 | _KVPair KV[_JSON_MaxPairs] // The actual Key:Value data 158 | } 159 | 160 | // --------------------------------------------------------------------------------------------------------------------- 161 | // JSON Parser Variables 162 | // --------------------------------------------------------------------------------------------------------------------- 163 | DEFINE_VARIABLE 164 | 165 | VOLATILE INTEGER nJSONDebug = 0 166 | 167 | 168 | // --------------------------------------------------------------------------------------------------------------------- 169 | // Helper Functions 170 | // --------------------------------------------------------------------------------------------------------------------- 171 | DEFINE_FUNCTION CHAR[_JSON_InputSize] _JSON_TrimWhiteSpace(CHAR cInput[]) 172 | // --------------------------------------------------------------------------------------------------------------------- 173 | // FUNCTION: _JSON_TrimWhiteSpace 174 | // PURPOSE: Special version of _TrimWhiteSpace with bigger return array size 175 | // Trims all whitespace characters from the beginning and end of string 176 | // 177 | // EXAMPLE: cReturn = _JSON_TrimWhiteSpace(' Hello ') 178 | // RETURNS: 'Hello' 179 | // --------------------------------------------------------------------------------------------------------------------- 180 | { 181 | STACK_VAR CHAR cTempInput[_JSON_InputSize] 182 | 183 | cTempInput = cInput 184 | 185 | WHILE(FIND_STRING(JSON_WhiteSpace,LEFT_STRING(cTempInput,1),1)) 186 | { 187 | cTempInput = RIGHT_STRING(cTempInput,LENGTH_STRING(cTempInput)-1) // Trim first byte 188 | } 189 | 190 | WHILE(FIND_STRING(JSON_WhiteSpace,RIGHT_STRING(cTempInput,1),1)) 191 | { 192 | cTempInput = LEFT_STRING(cTempInput,LENGTH_STRING(cTempInput)-1) // Trim last byte 193 | } 194 | 195 | RETURN cTempInput 196 | } 197 | 198 | // --------------------------------------------------------------------------------------------------------------------- 199 | // JSON Functions 200 | // --------------------------------------------------------------------------------------------------------------------- 201 | 202 | DEFINE_FUNCTION INTEGER JSON_ParseObject (CHAR cInput[],_JSON_Object jObject) 203 | // --------------------------------------------------------------------------------------------------------------------- 204 | // FUNCTION: JSON_ParseObject 205 | // PURPOSE: Replaces the old JSON_ParseObject; acts as a wrapper for the Validate and Parse functions so that we can 206 | // use JSON_ParseValidObject without having to run the validation routine 207 | // 208 | // EXAMPLE: nResult = JSON_ParseObject(cInput,jObject) 209 | // RETURNS: 1 if valid; cJSON_String is updated with JSON string if found 210 | // 0 if not valid; cJSON_String is updated with error 211 | // --------------------------------------------------------------------------------------------------------------------- 212 | { 213 | STACK_VAR INTEGER nReturn // Return value from JSON_Validate 214 | STACK_VAR CHAR cTempInput[_JSON_InputSize] // Copy cInput so we're not modifying the input parameter 215 | STACK_VAR CHAR cValidated[_JSON_InputSize] // The validated JSON string or error 216 | 217 | cTempInput = cInput 218 | nReturn = JSON_Validate(cTempInput,cValidated) // Check the string to make sure it's valid 219 | 220 | IF (!nReturn) // The JSON was not valid; return the error 221 | { 222 | SEND_STRING 0,"'[JSON] Validator returned the following error: ',cValidated" 223 | RETURN 0 224 | } 225 | ELSE // The JSON was valid, parse it 226 | { 227 | nReturn = JSON_ParseValidObject(cValidated,jObject) 228 | RETURN nReturn 229 | } 230 | } 231 | 232 | DEFINE_FUNCTION INTEGER JSON_FindObjectInString(CHAR cInput[],CHAR cJSON_String[]) 233 | // --------------------------------------------------------------------------------------------------------------------- 234 | // FUNCTION: JSON_FindObjectInString 235 | // PURPOSE: Traverses a string input to a) verify there is an object in the string and b) return the first valid JSON 236 | // string found. 237 | // 238 | // EXAMPLE: nResult = JSON_FindObjectInString(cDeviceBuffer,cJSON_String) 239 | // RETURNS: 1 if valid; cJSON_String is updated with JSON string if found 240 | // 0 if not valid; cJSON_String is updated with error 241 | // --------------------------------------------------------------------------------------------------------------------- 242 | { 243 | STACK_VAR CHAR cTempInput[_JSON_InputSize] // Temporary string storage 244 | STACK_VAR CHAR cTempOutput[_JSON_InputSize] // Temporary string storage 245 | STACK_VAR INTEGER F1 // Loop over bytes in string 246 | STACK_VAR INTEGER nFind // Find location 247 | STACK_VAR INTEGER bInKey // Currently in the key 248 | STACK_VAR INTEGER bInValue // Currently in the value 249 | STACK_VAR INTEGER bEndOfValue // Value should have ended 250 | STACK_VAR INTEGER bInObj // Currently in an object 251 | STACK_VAR INTEGER bInArray // Currently in an array 252 | STACK_VAR INTEGER BrObj // Current number of open object brackets 253 | STACK_VAR INTEGER BrArray // Current number of open array brackets 254 | 255 | cTempInput = _JSON_TrimWhiteSpace(cInput) 256 | F1 = 1 257 | 258 | WHILE (F1 <= LENGTH_STRING(cTempInput)) 259 | { 260 | SWITCH (cTempInput[F1]) 261 | { 262 | CASE $0D: 263 | CASE $0A: 264 | CASE $20: 265 | CASE $09: 266 | { 267 | // Ignore, we don't want this in our validated output 268 | } 269 | CASE '"': 270 | { 271 | nFind = FIND_STRING(cTempInput,'"',F1+1) 272 | IF (nFind) 273 | { 274 | cTempOutput = "cTempOutput,MID_STRING(cTempInput,F1,nFind-(F1-1))" 275 | F1 = nFind 276 | } 277 | ELSE 278 | { 279 | cJSON_String = "'ERROR at position ',ITOA(F1),': No string terminator'" 280 | RETURN 0 281 | } 282 | } 283 | CASE ':': 284 | { 285 | cTempOutput = "cTempOutput,cTempInput[F1]" 286 | 287 | IF (bInKey) // A : should only occur between the key and value in a pair. If it happens anywhere else not in a string, it's invalid 288 | { 289 | bInKey = 0 290 | bInValue = 1 291 | } 292 | ELSE 293 | { 294 | cJSON_String = "'ERROR at position ',ITOA(F1),': Colon in value or invalid position'" 295 | RETURN 0 296 | } 297 | } 298 | CASE ',': // A , should only occur between key:value pairs to separate them, or inside an array to separate values. If it happens anywhere else not in a string, it's invalid 299 | { 300 | cTempOutput = "cTempOutput,cTempInput[F1]" 301 | 302 | IF (bInArray) // This occured inside an array which is valid 303 | { 304 | // This is fine, ignore it 305 | } 306 | ELSE IF (bInValue) // This occurred at the end of the value, transition back to looking for key 307 | { 308 | bInValue = 0 309 | bInKey = 1 310 | } 311 | ELSE 312 | { 313 | cJSON_String = "'ERROR at position ',ITOA(F1),': Comma only valid between key:value or array values.'" 314 | RETURN 0 315 | } 316 | } 317 | CASE '{': // Start of object 318 | { 319 | cTempOutput = "cTempOutput,cTempInput[F1]" 320 | BrObj++ 321 | bInObj = 1 322 | bInArray = 0 323 | bInKey = 1 324 | bInValue = 0 325 | } 326 | CASE '}': // End of object 327 | { 328 | cTempOutput = "cTempOutput,cTempInput[F1]" 329 | IF (BrObj) // Make sure we've entered an object before this point; if not, it's not valid JSON 330 | { 331 | BrObj-- 332 | bInObj = 0 333 | } 334 | ELSE 335 | { 336 | cJSON_String = "'ERROR at position ',ITOA(F1),': End of object unexpected.'" 337 | RETURN 0 338 | } 339 | } 340 | CASE '[': // Start of array 341 | { 342 | cTempOutput = "cTempOutput,cTempInput[F1]" 343 | IF (bInValue) // Make sure we're looking at the value of the pair; if not, it's not valid JSON 344 | { 345 | BrArray++ 346 | bInArray = 1 347 | } 348 | ELSE 349 | { 350 | cJSON_String = "'ERROR at position ',ITOA(F1),': Start of array only valid in value.'" 351 | RETURN 0 352 | } 353 | } 354 | CASE ']': 355 | { 356 | cTempOutput = "cTempOutput,cTempInput[F1]" 357 | 358 | IF (BrArray) // Make sure we've entered an array before this point; if not, it's not valid JSON 359 | { 360 | BrArray-- 361 | bInArray = 0 362 | } 363 | ELSE 364 | { 365 | cJSON_String = "'ERROR at position ',ITOA(F1),': End of array unexpected.'" 366 | RETURN 0 367 | } 368 | } 369 | CASE 'T': // True 370 | CASE 't': // true 371 | { 372 | IF (bInValue) 373 | { 374 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,4)) == 'true') 375 | { 376 | cTempOutput = "cTempOutput,'true'" 377 | F1 = F1 + 3 378 | } 379 | ELSE 380 | { 381 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character t.'" 382 | RETURN 0 383 | } 384 | } 385 | ELSE 386 | { 387 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character t.'" 388 | RETURN 0 389 | } 390 | } 391 | CASE 'F': // False 392 | CASE 'f': // false 393 | { 394 | IF (bInValue) 395 | { 396 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,5)) == 'false') 397 | { 398 | cTempOutput = "cTempOutput,'false'" 399 | F1 = F1 + 4 400 | } 401 | ELSE 402 | { 403 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character f.'" 404 | RETURN 0 405 | } 406 | } 407 | ELSE 408 | { 409 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character f.'" 410 | RETURN 0 411 | } 412 | } 413 | CASE 'N': // Null 414 | CASE 'n': // null 415 | { 416 | IF (bInValue) 417 | { 418 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,4)) == 'null') 419 | { 420 | cTempOutput = "cTempOutput,'null'" 421 | F1 = F1 + 3 422 | } 423 | ELSE 424 | { 425 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character n.'" 426 | RETURN 0 427 | } 428 | } 429 | ELSE 430 | { 431 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character n.'" 432 | RETURN 0 433 | } 434 | } 435 | CASE '-': // Number Value 436 | CASE '1': // Number value 437 | CASE '2': // Number value 438 | CASE '3': // Number value 439 | CASE '4': // Number value 440 | CASE '5': // Number value 441 | CASE '6': // Number value 442 | CASE '7': // Number value 443 | CASE '8': // Number value 444 | CASE '9': // Number value 445 | CASE '0': // Number value 446 | { 447 | cTempOutput = "cTempOutput,cTempInput[F1]" 448 | 449 | IF (!bInValue) // Only time a number outside a string is valid is in the value, also in an array which is in the value 450 | { 451 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected integer in key.'" 452 | RETURN 0 453 | } 454 | } 455 | CASE '.': // A period is only valid outside a string in a number value 456 | { 457 | cTempOutput = "cTempOutput,cTempInput[F1]" 458 | 459 | IF (!bInValue) // Only time a period outside a string is valid is in the value when it's a number 460 | { 461 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected period in key.'" 462 | RETURN 0 463 | } 464 | } 465 | DEFAULT: 466 | { 467 | cJSON_String = "'ERROR at position ',ITOA(F1),': Unexpected character.'" 468 | RETURN 0 469 | } 470 | } 471 | 472 | IF (!BrObj && !BrArray) 473 | { 474 | cJSON_String = cTempOutput 475 | cInput = RIGHT_STRING(cTempInput,LENGTH_STRING(cTempInput) - F1) 476 | RETURN 1 477 | } 478 | ELSE 479 | { 480 | F1++ 481 | } 482 | } 483 | 484 | cJSON_String = "'End of string. No valid JSON strings found.'" 485 | RETURN 0 486 | } 487 | 488 | DEFINE_FUNCTION INTEGER JSON_Validate(CHAR cInput[],CHAR cCleaned[]) 489 | // --------------------------------------------------------------------------------------------------------------------- 490 | // FUNCTION: JSON_Validate 491 | // PURPOSE: Parses cInput to validate that it is a valid JSON structure; this also cleans out all unnecessary 492 | // whitespace characters and returns the cleaned string in the cCleaned parameter 493 | // 494 | // EXAMPLE: nResult = JSON_Validate(cDeviceBuffer,cValidated) 495 | // RETURNS: 1 if valid, 0 if not; cValidated is updated with compacted text 496 | // --------------------------------------------------------------------------------------------------------------------- 497 | { 498 | STACK_VAR CHAR cTempInput[_JSON_InputSize] // Temporary string storage 499 | STACK_VAR CHAR cTempOutput[_JSON_InputSize] // Temporary string storage 500 | STACK_VAR INTEGER F1 // Loop over bytes in string 501 | STACK_VAR INTEGER nFind // Find location 502 | STACK_VAR INTEGER bInKey // Currently in the key 503 | STACK_VAR INTEGER bInValue // Currently in the value 504 | STACK_VAR INTEGER bEndOfValue // Value should have ended 505 | STACK_VAR INTEGER bInObj // Currently in an object 506 | STACK_VAR INTEGER bInArray // Currently in an array 507 | STACK_VAR INTEGER BrObj // Current number of open object brackets 508 | STACK_VAR INTEGER BrArray // Current number of open array brackets 509 | 510 | cTempInput = _JSON_TrimWhiteSpace(cInput) 511 | F1 = 1 512 | 513 | WHILE (F1 <= LENGTH_STRING(cTempInput)) 514 | { 515 | SWITCH (cTempInput[F1]) 516 | { 517 | CASE $0D: 518 | CASE $0A: 519 | CASE $20: 520 | CASE $09: 521 | { 522 | // Ignore, we don't want this in our validated output 523 | } 524 | CASE '"': 525 | { 526 | nFind = FIND_STRING(cTempInput,'"',F1+1) 527 | IF (nFind) 528 | { 529 | cTempOutput = "cTempOutput,MID_STRING(cTempInput,F1,nFind-(F1-1))" 530 | F1 = nFind 531 | } 532 | ELSE 533 | { 534 | cCleaned = "'ERROR at position ',ITOA(F1),': No string terminator'" 535 | RETURN 0 536 | } 537 | } 538 | CASE ':': 539 | { 540 | cTempOutput = "cTempOutput,cTempInput[F1]" 541 | 542 | IF (bInKey) // A : should only occur between the key and value in a pair. If it happens anywhere else not in a string, it's invalid 543 | { 544 | bInKey = 0 545 | bInValue = 1 546 | } 547 | ELSE 548 | { 549 | cCleaned = "'ERROR at position ',ITOA(F1),': Colon in value or invalid position'" 550 | RETURN 0 551 | } 552 | } 553 | CASE ',': // A , should only occur between key:value pairs to separate them, or inside an array to separate values. If it happens anywhere else not in a string, it's invalid 554 | { 555 | cTempOutput = "cTempOutput,cTempInput[F1]" 556 | 557 | IF (bInArray) // This occured inside an array which is valid 558 | { 559 | // This is fine, ignore it 560 | } 561 | ELSE IF (bInValue) // This occurred at the end of the value, transition back to looking for key 562 | { 563 | bInValue = 0 564 | bInKey = 1 565 | } 566 | ELSE 567 | { 568 | cCleaned = "'ERROR at position ',ITOA(F1),': Comma only valid between key:value or array values.'" 569 | RETURN 0 570 | } 571 | } 572 | CASE '{': // Start of object 573 | { 574 | cTempOutput = "cTempOutput,cTempInput[F1]" 575 | BrObj++ 576 | bInObj = 1 577 | bInArray = 0 578 | bInKey = 1 579 | bInValue = 0 580 | } 581 | CASE '}': // End of object 582 | { 583 | cTempOutput = "cTempOutput,cTempInput[F1]" 584 | IF (BrObj) // Make sure we've entered an object before this point; if not, it's not valid JSON 585 | { 586 | BrObj-- 587 | bInObj = 0 588 | } 589 | ELSE 590 | { 591 | cCleaned = "'ERROR at position ',ITOA(F1),': End of object unexpected.'" 592 | RETURN 0 593 | } 594 | } 595 | CASE '[': // Start of array 596 | { 597 | cTempOutput = "cTempOutput,cTempInput[F1]" 598 | IF (bInValue) // Make sure we're looking at the value of the pair; if not, it's not valid JSON 599 | { 600 | BrArray++ 601 | bInArray = 1 602 | } 603 | ELSE 604 | { 605 | cCleaned = "'ERROR at position ',ITOA(F1),': Start of array only valid in value.'" 606 | RETURN 0 607 | } 608 | } 609 | CASE ']': 610 | { 611 | cTempOutput = "cTempOutput,cTempInput[F1]" 612 | 613 | IF (BrArray) // Make sure we've entered an array before this point; if not, it's not valid JSON 614 | { 615 | BrArray-- 616 | bInArray = 0 617 | } 618 | ELSE 619 | { 620 | cCleaned = "'ERROR at position ',ITOA(F1),': End of array unexpected.'" 621 | RETURN 0 622 | } 623 | } 624 | CASE 'T': // True 625 | CASE 't': // true 626 | { 627 | IF (bInValue) 628 | { 629 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,4)) == 'true') 630 | { 631 | cTempOutput = "cTempOutput,'true'" 632 | F1 = F1 + 3 633 | } 634 | ELSE 635 | { 636 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character t.'" 637 | RETURN 0 638 | } 639 | } 640 | ELSE 641 | { 642 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character t.'" 643 | RETURN 0 644 | } 645 | } 646 | CASE 'F': // False 647 | CASE 'f': // false 648 | { 649 | IF (bInValue) 650 | { 651 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,5)) == 'false') 652 | { 653 | cTempOutput = "cTempOutput,'false'" 654 | F1 = F1 + 4 655 | } 656 | ELSE 657 | { 658 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character f.'" 659 | RETURN 0 660 | } 661 | } 662 | ELSE 663 | { 664 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character f.'" 665 | RETURN 0 666 | } 667 | } 668 | CASE 'N': // Null 669 | CASE 'n': // null 670 | { 671 | IF (bInValue) 672 | { 673 | IF (LOWER_STRING(MID_STRING(cTempInput,F1,4)) == 'null') 674 | { 675 | cTempOutput = "cTempOutput,'null'" 676 | F1 = F1 + 3 677 | } 678 | ELSE 679 | { 680 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character n.'" 681 | RETURN 0 682 | } 683 | } 684 | ELSE 685 | { 686 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character n.'" 687 | RETURN 0 688 | } 689 | } 690 | CASE '-': // Number Value 691 | CASE '1': // Number value 692 | CASE '2': // Number value 693 | CASE '3': // Number value 694 | CASE '4': // Number value 695 | CASE '5': // Number value 696 | CASE '6': // Number value 697 | CASE '7': // Number value 698 | CASE '8': // Number value 699 | CASE '9': // Number value 700 | CASE '0': // Number value 701 | { 702 | cTempOutput = "cTempOutput,cTempInput[F1]" 703 | 704 | IF (!bInValue) // Only time a number outside a string is valid is in the value, also in an array which is in the value 705 | { 706 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected integer in key.'" 707 | RETURN 0 708 | } 709 | } 710 | CASE '.': // A period is only valid outside a string in a number value 711 | { 712 | cTempOutput = "cTempOutput,cTempInput[F1]" 713 | 714 | IF (!bInValue) // Only time a period outside a string is valid is in the value when it's a number 715 | { 716 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected period in key.'" 717 | RETURN 0 718 | } 719 | } 720 | DEFAULT: 721 | { 722 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected character.'" 723 | RETURN 0 724 | } 725 | } 726 | 727 | F1++ 728 | } 729 | 730 | IF (BrObj || BrArray) 731 | { 732 | cCleaned = "'ERROR at position ',ITOA(F1),': Unexpected end of JSON data.'" 733 | RETURN 0 734 | } 735 | ELSE 736 | { 737 | cCleaned = cTempOutput 738 | RETURN 1 739 | } 740 | } 741 | 742 | 743 | DEFINE_FUNCTION INTEGER JSON_ParseValidObject(CHAR cValidJSON[],_JSON_Object jObject) 744 | // --------------------------------------------------------------------------------------------------------------------- 745 | // FUNCTION: JSON_ParseValidObject 746 | // PURPOSE: Parses cValidJSON and puts the Key:Value pairs in the _JSON_Object that is passed in the second parameter 747 | // 748 | // EXAMPLE: nResult = JSON_ParseObject(cDeviceBuffer,jObject) 749 | // RETURNS: number of key:value pairs parsed; jObject has the parsed data 750 | // --------------------------------------------------------------------------------------------------------------------- 751 | { 752 | STACK_VAR _JSON_Object jReturn // Local storage of JSON data 753 | STACK_VAR CHAR cTempInput[_JSON_InputSize] // Temporary string storage 754 | STACK_VAR CHAR cValidated[_JSON_InputSize] // Validated JSON data 755 | STACK_VAR CHAR cTemp[5000] // Temporary string storage 756 | STACK_VAR INTEGER nReturn // Return from the validator 757 | STACK_VAR INTEGER F1 // Loop over bytes in string 758 | STACK_VAR INTEGER nFind // Find location 759 | STACK_VAR INTEGER bInKey // Currently in the key 760 | STACK_VAR INTEGER bInValue // Currently in the value 761 | STACK_VAR INTEGER bEndOfValue // Value should have ended 762 | STACK_VAR INTEGER bInObj // Currently in an object 763 | STACK_VAR INTEGER bInChildObj // Currently in a child object 764 | STACK_VAR INTEGER bInArray // Currently in an array 765 | STACK_VAR INTEGER BrObj // Current number of open object brackets 766 | STACK_VAR INTEGER BrChildObj // Current number of open child object brackets 767 | STACK_VAR INTEGER nChildObjStart // Start of the current child object 768 | STACK_VAR INTEGER BrArray // Current number of open array brackets 769 | STACK_VAR INTEGER BrArrayStart // Start of the current array 770 | STACK_VAR INTEGER nCurrentKV // Current KV pair we're processing 771 | 772 | cValidated = cValidJSON 773 | 774 | IF (jObject.Count) 775 | { 776 | JSON_ClearObject(jObject) 777 | } 778 | 779 | nCurrentKV = 1 780 | F1 = 1 781 | 782 | WHILE (F1 <= LENGTH_STRING(cValidated)) // Loop through string and look at almost every character 783 | { 784 | SWITCH (cValidated[F1]) 785 | { 786 | CASE '"': 787 | { 788 | IF (!bInChildObj && !bInArray) // Looking for strings in either the Key position, or single string values in the Value position 789 | { 790 | nFind = FIND_STRING(cValidated,'"',F1+1) 791 | cTemp = MID_STRING(cValidated,F1+1,nFind-(F1+1)) 792 | IF (bInKey) 793 | { 794 | IF (LENGTH_STRING(jReturn.KV[nCurrentKV].K)) // If we've already set a key name in the current index, increment the index; this is where we move to the next K:V pair 795 | { 796 | nCurrentKV++ 797 | } 798 | jReturn.KV[nCurrentKV].K = cTemp 799 | } 800 | ELSE IF (bInValue && !bInArray && !bInChildObj) 801 | { 802 | jReturn.KV[nCurrentKV].V = cTemp 803 | jReturn.KV[nCurrentKV].nType = _JSON_IsString 804 | } 805 | F1 = nFind 806 | } 807 | } 808 | CASE ':': 809 | { 810 | IF (!bInChildObj && !bInArray) // Child 811 | { 812 | IF (bInKey) // A : should only occur between the key and value in a pair. If it happens anywhere else not in a string, it's invalid 813 | { 814 | bInKey = 0 815 | bInValue = 1 816 | } 817 | ELSE 818 | { 819 | RETURN 0 820 | } 821 | } 822 | } 823 | CASE ',': // A , should only occur between key:value pairs to separate them, or inside an array to separate values. If it happens anywhere else not in a string, it's invalid 824 | { 825 | IF (!bInChildObj && !bInArray) 826 | { 827 | bInValue = 0 828 | bInKey = 1 829 | } 830 | } 831 | CASE '{': // Start of object 832 | { 833 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a { at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 834 | IF (!bInArray) 835 | { 836 | IF (bInChildObj) 837 | { 838 | BrChildObj++ 839 | } 840 | ELSE 841 | { 842 | IF (bInValue) 843 | { 844 | BrChildObj++ // This will cause all data to get copied to the value of the current key until we reach the end of the current object 845 | nChildObjStart = F1 846 | bInChildObj = 1 847 | } 848 | ELSE 849 | { 850 | BrObj++ 851 | BrChildObj = 0 852 | bInObj = 1 853 | bInArray = 0 854 | bInKey = 1 855 | bInValue = 0 856 | } 857 | } 858 | } 859 | } 860 | CASE '}': // End of object 861 | { 862 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a } at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 863 | IF (!bInArray) 864 | { 865 | IF (bInChildObj) 866 | { 867 | BrChildObj -- 868 | IF (BrChildObj == 0) // This is the end of the child object 869 | { 870 | nFind = F1 871 | jReturn.KV[nCurrentKV].nType = _JSON_IsObject 872 | jReturn.KV[nCurrentKV].V = MID_STRING(cValidated,nChildObjStart,nFind-(nChildObjStart-1)) 873 | nChildObjStart = 0 874 | bInChildObj = 0 875 | } 876 | } 877 | ELSE IF (BrObj) // Make sure we've entered a object before this point; if not, it's not valid JSON 878 | { 879 | BrObj-- 880 | bInObj = 0 881 | } 882 | } 883 | } 884 | CASE '[': // Start of array 885 | { 886 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a [ at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 887 | IF (!bInChildObj) 888 | { 889 | BrArray++ 890 | bInArray = 1 891 | IF (BrArray == 1) // Outer array 892 | { 893 | BrArrayStart = F1 894 | } 895 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] Array start at ',ITOA(F1),': BrArray: ',ITOA(BrArray),', BrArrayStart: ',ITOA(BrArrayStart)" 896 | } 897 | } 898 | CASE ']': // End of Array 899 | { 900 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a ] at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 901 | IF (!bInChildObj) 902 | { 903 | BrArray-- 904 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] Array stop at ',ITOA(F1),': BrArray: ',ITOA(BrArray),', BrArrayStart: ',ITOA(BrArrayStart)" 905 | IF (BrArray == 0) 906 | { 907 | nFind = F1 908 | jReturn.KV[nCurrentKV].V = MID_STRING(cValidated,BrArrayStart,nFind-(BrArrayStart-1)) 909 | jReturn.KV[nCurrentKV].nType = _JSON_IsArray 910 | BrArrayStart = 0 911 | bInArray = 0 912 | } 913 | } 914 | } 915 | CASE 't': // true 916 | { 917 | IF (!bInChildObj && !bInArray) 918 | { 919 | jReturn.KV[nCurrentKV].nType = _JSON_IsBool 920 | jReturn.KV[nCurrentKV].V = 'true' 921 | F1 = F1 + 3 922 | } 923 | } 924 | CASE 'f': // false 925 | { 926 | IF (!bInChildObj && !bInArray) 927 | { 928 | jReturn.KV[nCurrentKV].nType = _JSON_IsBool 929 | jReturn.KV[nCurrentKV].V = 'false' 930 | F1 = F1 + 4 931 | } 932 | } 933 | CASE 'n': // null 934 | { 935 | IF (!bInChildObj && !bInArray) 936 | { 937 | jReturn.KV[nCurrentKV].nType = _JSON_IsNull 938 | jReturn.KV[nCurrentKV].V = 'null' 939 | F1 = F1 + 3 940 | } 941 | } 942 | CASE '-': // Number value 943 | CASE '1': // Number value 944 | CASE '2': // Number value 945 | CASE '3': // Number value 946 | CASE '4': // Number value 947 | CASE '5': // Number value 948 | CASE '6': // Number value 949 | CASE '7': // Number value 950 | CASE '8': // Number value 951 | CASE '9': // Number value 952 | CASE '0': // Number value 953 | { 954 | IF (!bInChildObj && !bInArray && bInValue) 955 | { 956 | nFind = FIND_STRING(cValidated,',',F1) // Find end of number by finding the end of the K:V pair 957 | IF (!nFind) 958 | { 959 | nFind = LENGTH_STRING(cValidated) // Object should end with a } so don't grab that 960 | } 961 | cTemp = MID_STRING(cValidated,F1,nFind-F1) 962 | jReturn.KV[nCurrentKV].nType = _JSON_IsNumber 963 | jReturn.KV[nCurrentKV].V = cTemp 964 | F1 = nFind 965 | bInValue = 0 966 | bInKey = 1 967 | } 968 | } 969 | } 970 | 971 | F1++ 972 | } 973 | 974 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] ',ITOA(F1),' bytes parsed.'" 975 | jReturn.Count = nCurrentKV 976 | jObject = jReturn 977 | RETURN nCurrentKV 978 | } 979 | 980 | DEFINE_FUNCTION INTEGER JSON_ParseArray(CHAR cInput[],_JSON_Array jReturn) 981 | // --------------------------------------------------------------------------------------------------------------------- 982 | // FUNCTION: JSON_ParseArray 983 | // PURPOSE: Parses a JSON array string and returns an array object of the values found 984 | // Must be formatted like JSON: [element,element,element] 985 | // 986 | // EXAMPLE: cResult = JSON_ParseArray(cJSONArray,jParsedArray) 987 | // RETURNS: return is number of values in array; jParsedArray is an array object of the values 988 | // --------------------------------------------------------------------------------------------------------------------- 989 | { 990 | STACK_VAR INTEGER F1 // Normal FOR loop var 991 | STACK_VAR INTEGER nFind // Temp search variable 992 | STACK_VAR CHAR cTemp[_JSON_ValueSize] // Temporary string 993 | STACK_VAR CHAR cTempInput[_JSON_ValueSize] // Trimmed copy of the input array 994 | STACK_VAR _JSON_Array jTempArray // Temporary array to store the values 995 | STACK_VAR INTEGER nValueCount // Number of values found so far in array 996 | STACK_VAR INTEGER bInObj // Currently in an object 997 | STACK_VAR INTEGER bInChildObj // Currently in a child object 998 | STACK_VAR INTEGER bInArray // Currently in an array 999 | STACK_VAR INTEGER BrObj // Current number of open object brackets 1000 | STACK_VAR INTEGER BrChildObj // Current number of open child object brackets 1001 | STACK_VAR INTEGER nChildObjStart // Start of the current child object 1002 | STACK_VAR INTEGER BrArray // Current number of open array brackets 1003 | STACK_VAR INTEGER BrArrayStart // Start of the current array 1004 | 1005 | // Clean up unnecessary whitespace around array 1006 | cTempInput = _JSON_TrimWhiteSpace(cInput) 1007 | 1008 | // Make sure it's an array 1009 | IF (!(LEFT_STRING(cTempInput,1) == '[' && RIGHT_STRING(cTempInput,1) == ']')) 1010 | { 1011 | SEND_STRING 0,"'[JSON] Array not enclosed in brackets.'" 1012 | RETURN 0 1013 | } 1014 | 1015 | // Remove the square brackets and clean up the whitespace again; now we should only have data 1016 | cTempInput = _JSON_TrimWhiteSpace(MID_STRING(cTempInput,2,LENGTH_STRING(cTempInput)-2)) 1017 | //SEND_STRING 0,"cTempInput" 1018 | 1019 | nValueCount = 1 1020 | F1 = 1 1021 | 1022 | WHILE (F1 <= LENGTH_STRING(cTempInput)) 1023 | { 1024 | //SEND_STRING 0,"'Checking: ',ITOA(F1),' ',cTempInput[F1]" 1025 | SWITCH (cTempInput[F1]) 1026 | { 1027 | CASE $0D: 1028 | CASE $0A: 1029 | CASE $20: 1030 | CASE $09: 1031 | { 1032 | // Ignore 1033 | } 1034 | CASE '"': 1035 | { 1036 | IF (!bInChildObj && !bInArray) 1037 | { 1038 | nFind = FIND_STRING(cTempInput,'"',F1+1) 1039 | cTemp = MID_STRING(cTempInput,F1+1,nFind-(F1+1)) 1040 | jTempArray.List[nValueCount].nType = _JSON_IsString 1041 | jTempArray.List[nValueCount].Value = cTemp 1042 | F1 = nFind 1043 | } 1044 | } 1045 | CASE ',': // A , that's not part of a string should only occur between value pairs to separate them 1046 | { 1047 | IF (!bInChildObj && !bInArray) 1048 | { 1049 | nValueCount++ 1050 | } 1051 | } 1052 | CASE '{': // Start of object 1053 | { 1054 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a { at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 1055 | IF (!bInArray) 1056 | { 1057 | IF (bInChildObj) 1058 | { 1059 | BrChildObj++ 1060 | } 1061 | ELSE 1062 | { 1063 | BrChildObj++ // This will cause all data to get copied to the value of the current key until we reach the end of the current object 1064 | nChildObjStart = F1 1065 | bInChildObj = 1 1066 | } 1067 | } 1068 | } 1069 | CASE '}': // End of object 1070 | { 1071 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a } at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 1072 | IF (!bInArray) 1073 | { 1074 | BrChildObj -- 1075 | IF (BrChildObj == 0) // This is the end of the child object 1076 | { 1077 | nFind = F1 1078 | jTempArray.List[nValueCount].nType = _JSON_IsObject 1079 | jTempArray.List[nValueCount].Value = MID_STRING(cTempInput,nChildObjStart,nFind-(nChildObjStart-1)) 1080 | nChildObjStart = 0 1081 | bInChildObj = 0 1082 | } 1083 | } 1084 | } 1085 | CASE '[': // Start of array 1086 | { 1087 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a [ at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 1088 | IF (!bInChildObj) 1089 | { 1090 | BrArray++ 1091 | bInArray = 1 1092 | IF (BrArray == 1) // Outer array 1093 | { 1094 | BrArrayStart = F1 1095 | } 1096 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] Array start at ',ITOA(F1),': BrArray: ',ITOA(BrArray),', BrArrayStart: ',ITOA(BrArrayStart)" 1097 | } 1098 | } 1099 | CASE ']': // End of Array 1100 | { 1101 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] I see a ] at ',ITOA(F1),' (bInArray: ',ITOA(bInArray),', bInChildObj: ',ITOA(bInChildObj),')'" 1102 | IF (!bInChildObj) 1103 | { 1104 | BrArray-- 1105 | IF (nJSONDebug) SEND_STRING 0,"'[JSON] Array stop at ',ITOA(F1),': BrArray: ',ITOA(BrArray),', BrArrayStart: ',ITOA(BrArrayStart)" 1106 | IF (BrArray == 0) 1107 | { 1108 | nFind = F1 1109 | jTempArray.List[nValueCount].nType = _JSON_IsArray 1110 | jTempArray.List[nValueCount].Value = MID_STRING(cTempInput,BrArrayStart,nFind-(BrArrayStart-1)) 1111 | BrArrayStart = 0 1112 | bInArray = 0 1113 | } 1114 | } 1115 | } 1116 | CASE 't': // true 1117 | { 1118 | IF (!bInChildObj && !bInArray) 1119 | { 1120 | jTempArray.List[nValueCount].nType = _JSON_IsBool 1121 | jTempArray.List[nValueCount].Value = 'true' 1122 | F1 = F1 + 3 1123 | } 1124 | } 1125 | CASE 'f': // false 1126 | { 1127 | IF (!bInChildObj && !bInArray) 1128 | { 1129 | jTempArray.List[nValueCount].nType = _JSON_IsBool 1130 | jTempArray.List[nValueCount].Value = 'false' 1131 | F1 = F1 + 4 1132 | } 1133 | } 1134 | CASE 'n': // null 1135 | { 1136 | IF (!bInChildObj && !bInArray) 1137 | { 1138 | jTempArray.List[nValueCount].nType = _JSON_IsNull 1139 | jTempArray.List[nValueCount].Value = 'null' 1140 | F1 = F1 + 3 1141 | } 1142 | } 1143 | CASE '-': // Number value 1144 | CASE '1': // Number value 1145 | CASE '2': // Number value 1146 | CASE '3': // Number value 1147 | CASE '4': // Number value 1148 | CASE '5': // Number value 1149 | CASE '6': // Number value 1150 | CASE '7': // Number value 1151 | CASE '8': // Number value 1152 | CASE '9': // Number value 1153 | CASE '0': // Number value 1154 | { 1155 | IF (!bInChildObj && !bInArray) 1156 | { 1157 | nFind = FIND_STRING(cTempInput,',',F1) // Find end of number by finding the comma 1158 | IF (!nFind) // Last value in the array 1159 | { 1160 | nFind = LENGTH_STRING(cTempInput) + 1 // We're making up for the fact the comma would be the first character after the number 1161 | } 1162 | cTemp = MID_STRING(cTempInput,F1,nFind-F1) 1163 | jTempArray.List[nValueCount].nType = _JSON_IsNumber 1164 | jTempArray.List[nValueCount].Value = cTemp 1165 | F1 = nFind - 1 1166 | } 1167 | } 1168 | } 1169 | 1170 | F1++ 1171 | } 1172 | 1173 | IF (nValueCount) 1174 | { 1175 | SET_LENGTH_ARRAY(jTempArray.List,nValueCount) 1176 | jTempArray.Count = nValueCount 1177 | jReturn = jTempArray 1178 | 1179 | RETURN nValueCount 1180 | } 1181 | ELSE 1182 | { 1183 | jReturn.List[1].Value = 'JSON_ParseArray: Not a valid array.' 1184 | RETURN 0 1185 | } 1186 | } 1187 | 1188 | DEFINE_FUNCTION INTEGER JSON_HasKey(_JSON_Object jObject,CHAR cKeyName[]) 1189 | // --------------------------------------------------------------------------------------------------------------------- 1190 | // FUNCTION: JSON_HasKey 1191 | // PURPOSE: Iterates through the KV pairs in the JSON object and returns true/false if the key is found or not 1192 | // 1193 | // EXAMPLE: cResult = JSON_HasKey(jData,'RoomName') 1194 | // RETURNS: <1 or 0> 1195 | // --------------------------------------------------------------------------------------------------------------------- 1196 | { 1197 | STACK_VAR INTEGER F1 // Normal FOR loop var 1198 | 1199 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1200 | { 1201 | IF (LOWER_STRING(jObject.KV[F1].K) = LOWER_STRING(cKeyName)) 1202 | { 1203 | RETURN 1 1204 | } 1205 | } 1206 | RETURN 0 1207 | } 1208 | 1209 | DEFINE_FUNCTION CHAR[_JSON_ValueSize] JSON_GetValue(_JSON_Object jObject,CHAR cKeyName[]) 1210 | // --------------------------------------------------------------------------------------------------------------------- 1211 | // FUNCTION: JSON_GetValue 1212 | // PURPOSE: Iterates through the KV pairs in the JSON object and returns the value of the Key if found 1213 | // 1214 | // EXAMPLE: cResult = JSON_GetValue(jData,'RoomName') 1215 | // RETURNS: 1216 | // --------------------------------------------------------------------------------------------------------------------- 1217 | { 1218 | STACK_VAR INTEGER F1 // Normal FOR loop var 1219 | 1220 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1221 | { 1222 | IF (LOWER_STRING(jObject.KV[F1].K) = LOWER_STRING(cKeyName)) 1223 | { 1224 | RETURN jObject.KV[F1].V 1225 | } 1226 | } 1227 | 1228 | #IF_DEFINED libSystemLog_v1_2_Met 1229 | SendToLog('JSON',"'JSON_GetValue: Key not found: ',cKeyName") 1230 | #END_IF 1231 | 1232 | RETURN '' 1233 | } 1234 | 1235 | DEFINE_FUNCTION SINTEGER JSON_SetValue(_JSON_Object jObject,CHAR cKeyName[],CHAR cValue[],INTEGER nType) 1236 | // --------------------------------------------------------------------------------------------------------------------- 1237 | // FUNCTION: JSON_SetValue 1238 | // PURPOSE: Creates a key, or updates a key, with the specified value and type 1239 | // 1240 | // EXAMPLE: nResult = JSON_SetValue(jData,'RoomName','Conference Room',_JSON_IsString) 1241 | // RETURNS: 1242 | // --------------------------------------------------------------------------------------------------------------------- 1243 | { 1244 | STACK_VAR INTEGER F1 // Normal FOR loop var 1245 | STACK_VAR CHAR cTempValue[_JSON_ValueSize] // Temporary storage 1246 | STACK_VAR CHAR cValidValue[_JSON_ValueSize] // Verified value 1247 | 1248 | cTempValue = _JSON_TrimWhiteSpace(cValue) 1249 | 1250 | SWITCH (nType) 1251 | { 1252 | CASE _JSON_IsArray: 1253 | { 1254 | IF (LEFT_STRING(cTempValue,1) == '[' && RIGHT_STRING(cTempValue,1) == ']') 1255 | { 1256 | cValidValue = cTempValue 1257 | } 1258 | ELSE 1259 | { 1260 | RETURN _JSON_InvalidValue 1261 | } 1262 | } 1263 | CASE _JSON_IsObject: 1264 | { 1265 | IF (LEFT_STRING(cTempValue,1) == '{' && RIGHT_STRING(cTempValue,1) == '}') 1266 | { 1267 | cValidValue = cTempValue 1268 | } 1269 | ELSE 1270 | { 1271 | RETURN _JSON_InvalidValue 1272 | } 1273 | } 1274 | CASE _JSON_IsBool: 1275 | { 1276 | SELECT 1277 | { 1278 | ACTIVE (LOWER_STRING(cTempValue) == 'true' || cTempValue == '1'): // We'll accept 0 and 1 as false and true 1279 | { 1280 | cValidValue = 'true' 1281 | } 1282 | ACTIVE (LOWER_STRING(cTempValue) == 'false' || cTempValue == '0'): 1283 | { 1284 | cValidValue = 'false' 1285 | } 1286 | ACTIVE (1): 1287 | { 1288 | RETURN _JSON_InvalidValue 1289 | } 1290 | } 1291 | } 1292 | CASE _JSON_IsNull: 1293 | { 1294 | cValidValue = 'null' 1295 | } 1296 | CASE _JSON_IsNumber: 1297 | { 1298 | IF (FTOA(ATOF(cTempValue)) == cTempValue) // Convert string to a float, then back to a string and see if it matches 1299 | { 1300 | cValidValue = cTempValue 1301 | } 1302 | ELSE 1303 | { 1304 | RETURN _JSON_InvalidValue 1305 | } 1306 | } 1307 | CASE _JSON_IsString: 1308 | { 1309 | cValidValue = cTempValue 1310 | } 1311 | } 1312 | 1313 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1314 | { 1315 | IF (LOWER_STRING(jObject.KV[F1].K) = LOWER_STRING(cKeyName)) 1316 | { 1317 | jObject.KV[F1].V = cValidValue 1318 | jObject.KV[F1].nType = nType 1319 | RETURN _JSON_Success 1320 | } 1321 | } 1322 | 1323 | IF (jObject.Count < _JSON_MaxPairs) 1324 | { 1325 | jObject.Count++ 1326 | jObject.KV[jObject.Count].K = cKeyName 1327 | jObject.KV[jObject.Count].V = cValidValue 1328 | jObject.KV[jObject.Count].nType = nType 1329 | RETURN _JSON_Success 1330 | } 1331 | ELSE 1332 | { 1333 | #IF_DEFINED libSystemLog_v1_2_Met 1334 | SendToLog('JSON',"'JSON_SetValue: Out of memory in JSON object while trying to add ',cKeyName,':',cValidValue") 1335 | #END_IF 1336 | 1337 | RETURN _JSON_OutOfMemory 1338 | } 1339 | } 1340 | 1341 | DEFINE_FUNCTION SINTEGER JSON_DeleteKey(_JSON_Object jObject,CHAR cKeyName[]) 1342 | // --------------------------------------------------------------------------------------------------------------------- 1343 | // FUNCTION: JSON_DeleteKey 1344 | // PURPOSE: Deletes a key from the object if found 1345 | // 1346 | // EXAMPLE: nResult = JSON_DeleteKey(jData,'RoomName') 1347 | // RETURNS: 1348 | // --------------------------------------------------------------------------------------------------------------------- 1349 | { 1350 | STACK_VAR INTEGER F1 // Normal FOR loop var 1351 | STACK_VAR INTEGER nIndex // Which index matched 1352 | STACK_VAR _KVPair KV // Empty KV pair 1353 | 1354 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1355 | { 1356 | IF (LOWER_STRING(jObject.KV[F1].K) = LOWER_STRING(cKeyName)) 1357 | { 1358 | nIndex = F1 1359 | BREAK 1360 | } 1361 | } 1362 | 1363 | IF (nIndex) 1364 | { 1365 | FOR (F1 = nIndex; F1 <= jObject.Count; F1++) 1366 | { 1367 | IF (F1 < _JSON_MaxPairs) 1368 | { 1369 | jObject.KV[F1] = jObject.KV[F1+1] 1370 | } 1371 | ELSE 1372 | { 1373 | jObject.KV[F1] = KV 1374 | } 1375 | } 1376 | RETURN _JSON_Success 1377 | } 1378 | ELSE 1379 | { 1380 | #IF_DEFINED libSystemLog_v1_2_Met 1381 | SendToLog('JSON',"'JSON_DeleteKey: Key not found: ',cKeyName") 1382 | #END_IF 1383 | 1384 | RETURN _JSON_KeyNotFound 1385 | } 1386 | } 1387 | 1388 | DEFINE_FUNCTION INTEGER JSON_GetType(_JSON_Object jObject,CHAR cKeyName[]) 1389 | // --------------------------------------------------------------------------------------------------------------------- 1390 | // FUNCTION: JSON_GetType 1391 | // PURPOSE: Iterates through the KV pairs in the JSON object and returns the type of the value of the Key if found 1392 | // 1393 | // EXAMPLE: cResult = JSON_GetValueType(jData,'RoomName') 1394 | // RETURNS: 1395 | // --------------------------------------------------------------------------------------------------------------------- 1396 | { 1397 | STACK_VAR INTEGER F1 // Normal FOR loop var 1398 | 1399 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1400 | { 1401 | IF (LOWER_STRING(jObject.KV[F1].K) = LOWER_STRING(cKeyName)) 1402 | { 1403 | RETURN jObject.KV[F1].nType 1404 | } 1405 | } 1406 | 1407 | #IF_DEFINED libSystemLog_v1_2_Met 1408 | SendToLog('JSON',"'JSON_GetType: Key not found: ',cKeyName") 1409 | #END_IF 1410 | 1411 | RETURN 0 1412 | } 1413 | 1414 | DEFINE_FUNCTION CHAR[_JSON_InputSize] JSON_ObjectToString(_JSON_Object jObject) 1415 | // --------------------------------------------------------------------------------------------------------------------- 1416 | // FUNCTION: JSON_ObjectToString 1417 | // PURPOSE: Output a JSON object with the records included in the _JSON_Object parameter 1418 | // 1419 | // EXAMPLE: JSON_ObjectToString(jData) 1420 | // RETURNS: 1421 | // --------------------------------------------------------------------------------------------------------------------- 1422 | { 1423 | STACK_VAR INTEGER F1 // Standard FOR loop variable 1424 | STACK_VAR CHAR cComma[1] // The comma, should we not be at the end of the array 1425 | STACK_VAR CHAR cReturn[_JSON_InputSize] // Temp storage for return variable 1426 | 1427 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1428 | { 1429 | cReturn = "cReturn,$09,'"',jObject.KV[F1].K,'": '" 1430 | 1431 | IF (F1 < jObject.Count) // Fun, huh? 1432 | { 1433 | cComma = ',' 1434 | } 1435 | ELSE 1436 | { 1437 | cComma = '' 1438 | } 1439 | 1440 | SWITCH (jObject.KV[F1].nType) 1441 | { 1442 | CASE _JSON_IsObject: 1443 | { 1444 | cReturn = "cReturn,jObject.KV[F1].V,cComma,13" 1445 | } 1446 | CASE _JSON_IsArray: 1447 | { 1448 | cReturn = "cReturn,jObject.KV[F1].V,cComma,13" 1449 | } 1450 | CASE _JSON_IsString: 1451 | { 1452 | cReturn = "cReturn,'"',jObject.KV[F1].V,'"',cComma,13" 1453 | } 1454 | CASE _JSON_IsNumber: 1455 | { 1456 | cReturn = "cReturn,jObject.KV[F1].V,cComma,13" 1457 | } 1458 | CASE _JSON_IsBool: 1459 | { 1460 | cReturn = "cReturn,jObject.KV[F1].V,cComma,13" 1461 | } 1462 | CASE _JSON_IsNull: 1463 | { 1464 | cReturn = "cReturn,jObject.KV[F1].V,cComma,13" 1465 | } 1466 | } 1467 | } 1468 | 1469 | cReturn = "'{',13,cReturn,'}'" 1470 | RETURN cReturn 1471 | } 1472 | 1473 | DEFINE_FUNCTION CHAR[_JSON_InputSize] JSON_ArrayToString(_JSON_Array jArray) 1474 | // --------------------------------------------------------------------------------------------------------------------- 1475 | // FUNCTION: JSON_ArrayToString 1476 | // PURPOSE: Output a JSON array with the elements included in the _JSON_Array parameter 1477 | // 1478 | // EXAMPLE: JSON_ArrayToString(jArray) 1479 | // RETURNS: 1480 | // --------------------------------------------------------------------------------------------------------------------- 1481 | { 1482 | STACK_VAR INTEGER F1 // Standard FOR loop variable 1483 | STACK_VAR CHAR cComma[1] // The comma, should we not be at the end of the array 1484 | STACK_VAR CHAR cReturn[_JSON_InputSize] // Temp storage for return variable 1485 | 1486 | FOR (F1 = 1; F1 <= jArray.Count; F1++) 1487 | { 1488 | IF (F1 < jArray.Count) // Fun, huh? 1489 | { 1490 | cComma = ',' 1491 | } 1492 | ELSE 1493 | { 1494 | cComma = '' 1495 | } 1496 | 1497 | SWITCH (jArray.List[F1].nType) 1498 | { 1499 | CASE _JSON_IsObject: 1500 | { 1501 | cReturn = "cReturn,jArray.List[F1].Value,cComma,13" 1502 | } 1503 | CASE _JSON_IsArray: 1504 | { 1505 | cReturn = "cReturn,jArray.List[F1].Value,cComma,13" 1506 | } 1507 | CASE _JSON_IsString: 1508 | { 1509 | cReturn = "cReturn,'"',jArray.List[F1].Value,'"',cComma,13" 1510 | } 1511 | CASE _JSON_IsNumber: 1512 | { 1513 | cReturn = "cReturn,jArray.List[F1].Value,cComma,13" 1514 | } 1515 | CASE _JSON_IsBool: 1516 | { 1517 | cReturn = "cReturn,jArray.List[F1].Value,cComma,13" 1518 | } 1519 | CASE _JSON_IsNull: 1520 | { 1521 | cReturn = "cReturn,jArray.List[F1].Value,cComma,13" 1522 | } 1523 | } 1524 | } 1525 | 1526 | cReturn = "'[',cReturn,']'" 1527 | RETURN cReturn 1528 | } 1529 | 1530 | DEFINE_FUNCTION INTEGER JSON_ClearObject(_JSON_Object jObject) 1531 | // --------------------------------------------------------------------------------------------------------------------- 1532 | // FUNCTION: JSON_ClearObject 1533 | // PURPOSE: Clear a _JSON_Object variable of all data, if using a global variable to store data; most of the time you 1534 | // should be using STACK_VARs which will be destroyed when the code block ends 1535 | // 1536 | // EXAMPLE: JSON_ClearObject(jData) 1537 | // RETURNS: Nothing 1538 | // --------------------------------------------------------------------------------------------------------------------- 1539 | { 1540 | STACK_VAR INTEGER F1 1541 | STACK_VAR _KVPair EmptyRecord 1542 | 1543 | FOR (F1 = 1; F1 <= jObject.Count; F1++) 1544 | { 1545 | jObject.KV[F1] = EmptyRecord 1546 | } 1547 | 1548 | jObject.Count = 0 1549 | } 1550 | 1551 | DEFINE_FUNCTION CHAR[_JSON_InputSize] NumArrayToJSON(SLONG nArray[],INTEGER nLength) 1552 | // --------------------------------------------------------------------------------------------------------------------- 1553 | // FUNCTION: NumArrayToJSON 1554 | // PURPOSE: Input a SLONG, LONG, or INTEGER array and return the JSON string equivalent of the array 1555 | // 1556 | // EXAMPLE: NumArrayToJSON(slArray,5) 1557 | // RETURNS: '[ 1, 2, 3, 4, 5 ]' 1558 | // --------------------------------------------------------------------------------------------------------------------- 1559 | { 1560 | STACK_VAR INTEGER F1 // Standard FOR loop variable 1561 | STACK_VAR CHAR cComma[1] // The comma, should we not be at the end of the array 1562 | STACK_VAR CHAR cReturn[_JSON_InputSize] // Temp storage for return variable 1563 | 1564 | IF (MAX_LENGTH_ARRAY(nArray) < nLength) 1565 | { 1566 | RETURN '' 1567 | } 1568 | 1569 | FOR (F1 = 1; F1 <= nLength; F1++) 1570 | { 1571 | IF (F1 < nLength) // Fun, huh? 1572 | { 1573 | cComma = ',' 1574 | } 1575 | ELSE 1576 | { 1577 | cComma = '' 1578 | } 1579 | 1580 | cReturn = "cReturn,ITOA(nArray[F1]),cComma" 1581 | } 1582 | 1583 | cReturn = "'[',cReturn,']'" 1584 | RETURN cReturn 1585 | } 1586 | 1587 | DEFINE_FUNCTION CHAR[_JSON_InputSize] StringArrayToJSON(CHAR cArray[][],INTEGER nLength) 1588 | // --------------------------------------------------------------------------------------------------------------------- 1589 | // FUNCTION: StringArrayToJSON 1590 | // PURPOSE: Input a CHAR array and return the JSON string equivalent of the array 1591 | // 1592 | // EXAMPLE: StringArrayToJSON(cArray,5) 1593 | // RETURNS: '[ "Hello", "my", "name", "is", "JSON" ]' 1594 | // --------------------------------------------------------------------------------------------------------------------- 1595 | { 1596 | STACK_VAR INTEGER F1 // Standard FOR loop variable 1597 | STACK_VAR CHAR cComma[1] // The comma, should we not be at the end of the array 1598 | STACK_VAR CHAR cReturn[_JSON_InputSize] // Temp storage for return variable 1599 | 1600 | IF (MAX_LENGTH_ARRAY(cArray) < nLength) 1601 | { 1602 | RETURN '' 1603 | } 1604 | 1605 | FOR (F1 = 1; F1 <= nLength; F1++) 1606 | { 1607 | IF (F1 < nLength) // Fun, huh? 1608 | { 1609 | cComma = ',' 1610 | } 1611 | ELSE 1612 | { 1613 | cComma = '' 1614 | } 1615 | 1616 | cReturn = "cReturn,'"',cArray[F1],'"',cComma" 1617 | } 1618 | 1619 | cReturn = "'[',cReturn,']'" 1620 | RETURN cReturn 1621 | } 1622 | --------------------------------------------------------------------------------