| @@ -103,4 +103,79 @@ | ||
| 103 | 103 | this.errors = []; |
| 104 | 104 | }.bind(this)); |
| 105 | 105 | } |
| 106 | 106 | }; |
| 107 | + | |
| 108 | +/** | |
| 109 | + * Parse JSON string, including automatically detecting unwanted extra input and skipping it | |
| 110 | + * | |
| 111 | + * @param {string|object} json_mix_str - JSON string which need to parse and convert to object | |
| 112 | + * | |
| 113 | + * @throws SyntaxError|String (including passing on what JSON.parse may throw) if a parsing error occurs. | |
| 114 | + * | |
| 115 | + * @return mixed parsed JSON object. Will only return if parsing is successful (otherwise, will throw) | |
| 116 | + */ | |
| 117 | +function wpo_parse_json(json_mix_str) { | |
| 118 | + // When using wp_send_json to return the value, the format is already parsed. | |
| 119 | + if ('object' === typeof json_mix_str) return json_mix_str; | |
| 120 | + | |
| 121 | + // Just try it - i.e. the 'default' case where things work (which can include extra whitespace/line-feeds, and simple strings, etc.). | |
| 122 | + try { | |
| 123 | + var result = JSON.parse(json_mix_str); | |
| 124 | + return result; | |
| 125 | + } catch (e) { | |
| 126 | + console.log("WPO: Exception when trying to parse JSON (1) - will attempt to fix/re-parse"); | |
| 127 | + console.log(json_mix_str); | |
| 128 | + } | |
| 129 | + | |
| 130 | + var json_start_pos = json_mix_str.indexOf('{'); | |
| 131 | + var json_last_pos = json_mix_str.lastIndexOf('}'); | |
| 132 | + | |
| 133 | + // Case where some php notice may be added after or before json string | |
| 134 | + if (json_start_pos > -1 && json_last_pos > -1) { | |
| 135 | + var json_str = json_mix_str.slice(json_start_pos, json_last_pos + 1); | |
| 136 | + try { | |
| 137 | + var parsed = JSON.parse(json_str); | |
| 138 | + return parsed; | |
| 139 | + } catch (e) { | |
| 140 | + console.log("WPO: Exception when trying to parse JSON (2) - will attempt to fix/re-parse based upon bracket counting"); | |
| 141 | + | |
| 142 | + var cursor = json_start_pos; | |
| 143 | + var open_count = 0; | |
| 144 | + var last_character = ''; | |
| 145 | + var inside_string = false; | |
| 146 | + | |
| 147 | + // Don't mistake this for a real JSON parser. Its aim is to improve the odds in real-world cases seen, not to arrive at universal perfection. | |
| 148 | + while ((open_count > 0 || cursor == json_start_pos) && cursor <= json_last_pos) { | |
| 149 | + | |
| 150 | + var current_character = json_mix_str.charAt(cursor); | |
| 151 | + | |
| 152 | + if (!inside_string && '{' == current_character) { | |
| 153 | + open_count++; | |
| 154 | + } else if (!inside_string && '}' == current_character) { | |
| 155 | + open_count--; | |
| 156 | + } else if ('"' == current_character && '\\' != last_character) { | |
| 157 | + inside_string = inside_string ? false : true; | |
| 158 | + } | |
| 159 | + | |
| 160 | + last_character = current_character; | |
| 161 | + cursor++; | |
| 162 | + } | |
| 163 | + | |
| 164 | + console.log("Started at cursor="+json_start_pos+", ended at cursor="+cursor+" with result following:"); | |
| 165 | + console.log(json_mix_str.substring(json_start_pos, cursor)); | |
| 166 | + | |
| 167 | + try { | |
| 168 | + var parsed = JSON.parse(json_mix_str.substring(json_start_pos, cursor)); | |
| 169 | + // console.log('WPO: JSON re-parse successful'); | |
| 170 | + return parsed; | |
| 171 | + } catch (e) { | |
| 172 | + // Throw it again, so that our function works just like JSON.parse() in its behaviour. | |
| 173 | + throw e; | |
| 174 | + } | |
| 175 | + | |
| 176 | + } | |
| 177 | + } | |
| 178 | + | |
| 179 | + throw "WPO: could not parse the JSON"; | |
| 180 | + | |
| 181 | +} | |