2014-02-09 22:10:30 -03:00
/**************************************************************************/
/* json.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-09 22:10:30 -03:00
# include "json.h"
2018-09-11 18:13:45 +02:00
2023-01-14 12:57:28 +01:00
# include "core/config/engine.h"
2020-11-07 19:33:38 -03:00
# include "core/string/print_string.h"
2014-02-09 22:10:30 -03:00
const char * JSON : : tk_name [ TK_MAX ] = {
" '{' " ,
" '}' " ,
" '[' " ,
" ']' " ,
" identifier " ,
" string " ,
" number " ,
" ':' " ,
" ',' " ,
" EOF " ,
} ;
2020-12-29 18:12:33 +00:00
String JSON : : _make_indent ( const String & p_indent , int p_size ) {
2023-01-29 00:52:05 +01:00
return p_indent . repeat ( p_size ) ;
2017-11-03 15:32:58 +08:00
}
2022-05-19 17:00:06 +02:00
String JSON : : _stringify ( const Variant & p_var , const String & p_indent , int p_cur_indent , bool p_sort_keys , HashSet < const void * > & p_markers , bool p_full_precision ) {
2022-09-19 18:10:23 +02:00
ERR_FAIL_COND_V_MSG ( p_cur_indent > Variant : : MAX_RECURSION_DEPTH , " ... " , " JSON structure is too deep. Bailing. " ) ;
2017-11-03 15:32:58 +08:00
String colon = " : " ;
String end_statement = " " ;
2020-12-15 12:04:21 +00:00
if ( ! p_indent . is_empty ( ) ) {
2017-11-03 15:32:58 +08:00
colon + = " " ;
end_statement + = " \n " ;
}
2014-02-09 22:10:30 -03:00
switch ( p_var . get_type ( ) ) {
2020-05-10 13:00:47 +02:00
case Variant : : NIL :
return " null " ;
case Variant : : BOOL :
return p_var . operator bool ( ) ? " true " : " false " ;
case Variant : : INT :
return itos ( p_var ) ;
2019-12-23 17:38:33 +02:00
case Variant : : FLOAT : {
double num = p_var ;
if ( p_full_precision ) {
// Store unreliable digits (17) instead of just reliable
// digits (14) so that the value can be decoded exactly.
return String : : num ( num , 17 - ( int ) floor ( log10 ( num ) ) ) ;
} else {
// Store only reliable digits (14) by default.
return String : : num ( num , 14 - ( int ) floor ( log10 ( num ) ) ) ;
}
}
2020-02-24 15:20:53 -03:00
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
2020-02-17 18:06:54 -03:00
case Variant : : PACKED_STRING_ARRAY :
2014-02-09 22:10:30 -03:00
case Variant : : ARRAY : {
2023-03-01 19:04:38 +01:00
Array a = p_var ;
if ( a . size ( ) = = 0 ) {
return " [] " ;
}
2014-02-09 22:10:30 -03:00
String s = " [ " ;
2017-11-03 15:32:58 +08:00
s + = end_statement ;
2021-05-26 22:29:32 +08:00
ERR_FAIL_COND_V_MSG ( p_markers . has ( a . id ( ) ) , " \" [...] \" " , " Converting circular structure to JSON. " ) ;
p_markers . insert ( a . id ( ) ) ;
2014-02-09 22:10:30 -03:00
for ( int i = 0 ; i < a . size ( ) ; i + + ) {
2017-11-03 15:32:58 +08:00
if ( i > 0 ) {
s + = " , " ;
s + = end_statement ;
}
2020-12-29 18:12:33 +00:00
s + = _make_indent ( p_indent , p_cur_indent + 1 ) + _stringify ( a [ i ] , p_indent , p_cur_indent + 1 , p_sort_keys , p_markers ) ;
2014-02-09 22:10:30 -03:00
}
2017-11-03 15:32:58 +08:00
s + = end_statement + _make_indent ( p_indent , p_cur_indent ) + " ] " ;
2021-05-26 22:29:32 +08:00
p_markers . erase ( a . id ( ) ) ;
2014-02-09 22:10:30 -03:00
return s ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case Variant : : DICTIONARY : {
String s = " { " ;
2017-11-03 15:32:58 +08:00
s + = end_statement ;
2014-02-09 22:10:30 -03:00
Dictionary d = p_var ;
2021-05-26 22:29:32 +08:00
ERR_FAIL_COND_V_MSG ( p_markers . has ( d . id ( ) ) , " \" {...} \" " , " Converting circular structure to JSON. " ) ;
p_markers . insert ( d . id ( ) ) ;
2014-02-09 22:10:30 -03:00
List < Variant > keys ;
d . get_key_list ( & keys ) ;
2020-05-14 16:41:43 +02:00
if ( p_sort_keys ) {
2017-11-03 15:32:58 +08:00
keys . sort ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-11-03 15:32:58 +08:00
2021-07-24 21:28:50 -04:00
bool first_key = true ;
2021-07-24 15:46:25 +02:00
for ( const Variant & E : keys ) {
2021-07-24 21:28:50 -04:00
if ( first_key ) {
first_key = false ;
} else {
2017-11-03 15:32:58 +08:00
s + = " , " ;
s + = end_statement ;
}
2021-07-15 23:45:57 -04:00
s + = _make_indent ( p_indent , p_cur_indent + 1 ) + _stringify ( String ( E ) , p_indent , p_cur_indent + 1 , p_sort_keys , p_markers ) ;
2017-11-03 15:32:58 +08:00
s + = colon ;
2021-07-15 23:45:57 -04:00
s + = _stringify ( d [ E ] , p_indent , p_cur_indent + 1 , p_sort_keys , p_markers ) ;
2014-02-09 22:10:30 -03:00
}
2017-11-03 15:32:58 +08:00
s + = end_statement + _make_indent ( p_indent , p_cur_indent ) + " } " ;
2021-05-26 22:29:32 +08:00
p_markers . erase ( d . id ( ) ) ;
2014-02-09 22:10:30 -03:00
return s ;
2020-05-19 15:46:49 +02:00
}
2020-05-10 13:00:47 +02:00
default :
return " \" " + String ( p_var ) . json_escape ( ) + " \" " ;
2014-02-09 22:10:30 -03:00
}
}
2020-07-27 13:43:20 +03:00
Error JSON : : _get_token ( const char32_t * p_str , int & index , int p_len , Token & r_token , int & line , String & r_err_str ) {
2017-01-15 16:59:02 +08:00
while ( p_len > 0 ) {
2017-08-11 15:10:05 -04:00
switch ( p_str [ index ] ) {
2014-02-09 22:10:30 -03:00
case ' \n ' : {
line + + ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
break ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case 0 : {
r_token . type = TK_EOF ;
return OK ;
} break ;
case ' { ' : {
r_token . type = TK_CURLY_BRACKET_OPEN ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' } ' : {
r_token . type = TK_CURLY_BRACKET_CLOSE ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' [ ' : {
r_token . type = TK_BRACKET_OPEN ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' ] ' : {
r_token . type = TK_BRACKET_CLOSE ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' : ' : {
r_token . type = TK_COLON ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' , ' : {
r_token . type = TK_COMMA ;
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
return OK ;
2020-05-19 15:46:49 +02:00
}
2014-02-09 22:10:30 -03:00
case ' " ' : {
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
String str ;
while ( true ) {
2017-08-11 15:10:05 -04:00
if ( p_str [ index ] = = 0 ) {
2014-02-09 22:10:30 -03:00
r_err_str = " Unterminated String " ;
return ERR_PARSE_ERROR ;
2017-08-11 15:10:05 -04:00
} else if ( p_str [ index ] = = ' " ' ) {
index + + ;
2014-02-09 22:10:30 -03:00
break ;
2017-08-11 15:10:05 -04:00
} else if ( p_str [ index ] = = ' \\ ' ) {
2014-02-09 22:10:30 -03:00
//escaped characters...
2017-08-11 15:10:05 -04:00
index + + ;
2020-07-27 13:43:20 +03:00
char32_t next = p_str [ index ] ;
2014-02-09 22:10:30 -03:00
if ( next = = 0 ) {
r_err_str = " Unterminated String " ;
return ERR_PARSE_ERROR ;
}
2020-07-27 13:43:20 +03:00
char32_t res = 0 ;
2014-02-09 22:10:30 -03:00
switch ( next ) {
2020-05-10 13:00:47 +02:00
case ' b ' :
res = 8 ;
break ;
case ' t ' :
res = 9 ;
break ;
case ' n ' :
res = 10 ;
break ;
case ' f ' :
res = 12 ;
break ;
case ' r ' :
res = 13 ;
break ;
2014-02-09 22:10:30 -03:00
case ' u ' : {
2020-02-13 11:37:37 +01:00
// hex number
2014-02-09 22:10:30 -03:00
for ( int j = 0 ; j < 4 ; j + + ) {
2020-07-27 13:43:20 +03:00
char32_t c = p_str [ index + j + 1 ] ;
2014-02-09 22:10:30 -03:00
if ( c = = 0 ) {
r_err_str = " Unterminated String " ;
return ERR_PARSE_ERROR ;
}
2022-02-04 10:32:20 +02:00
if ( ! is_hex_digit ( c ) ) {
2014-02-09 22:10:30 -03:00
r_err_str = " Malformed hex constant in string " ;
return ERR_PARSE_ERROR ;
}
2020-07-27 13:43:20 +03:00
char32_t v ;
2022-02-04 10:32:20 +02:00
if ( is_digit ( c ) ) {
2014-02-09 22:10:30 -03:00
v = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
v = c - ' a ' ;
v + = 10 ;
} else if ( c > = ' A ' & & c < = ' F ' ) {
v = c - ' A ' ;
v + = 10 ;
} else {
2020-02-13 11:37:37 +01:00
ERR_PRINT ( " Bug parsing hex constant. " ) ;
2014-02-09 22:10:30 -03:00
v = 0 ;
}
res < < = 4 ;
res | = v ;
}
2017-08-11 15:10:05 -04:00
index + = 4 ; //will add at the end anyway
2014-02-09 22:10:30 -03:00
2021-03-01 13:23:12 +02:00
if ( ( res & 0xfffffc00 ) = = 0xd800 ) {
if ( p_str [ index + 1 ] ! = ' \\ ' | | p_str [ index + 2 ] ! = ' u ' ) {
r_err_str = " Invalid UTF-16 sequence in string, unpaired lead surrogate " ;
return ERR_PARSE_ERROR ;
}
index + = 2 ;
char32_t trail = 0 ;
for ( int j = 0 ; j < 4 ; j + + ) {
char32_t c = p_str [ index + j + 1 ] ;
if ( c = = 0 ) {
r_err_str = " Unterminated String " ;
return ERR_PARSE_ERROR ;
}
2022-02-04 10:32:20 +02:00
if ( ! is_hex_digit ( c ) ) {
2021-03-01 13:23:12 +02:00
r_err_str = " Malformed hex constant in string " ;
return ERR_PARSE_ERROR ;
}
char32_t v ;
2022-02-04 10:32:20 +02:00
if ( is_digit ( c ) ) {
2021-03-01 13:23:12 +02:00
v = c - ' 0 ' ;
} else if ( c > = ' a ' & & c < = ' f ' ) {
v = c - ' a ' ;
v + = 10 ;
} else if ( c > = ' A ' & & c < = ' F ' ) {
v = c - ' A ' ;
v + = 10 ;
} else {
ERR_PRINT ( " Bug parsing hex constant. " ) ;
v = 0 ;
}
trail < < = 4 ;
trail | = v ;
}
if ( ( trail & 0xfffffc00 ) = = 0xdc00 ) {
res = ( res < < 10UL ) + trail - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
index + = 4 ; //will add at the end anyway
} else {
r_err_str = " Invalid UTF-16 sequence in string, unpaired lead surrogate " ;
return ERR_PARSE_ERROR ;
}
} else if ( ( res & 0xfffffc00 ) = = 0xdc00 ) {
r_err_str = " Invalid UTF-16 sequence in string, unpaired trail surrogate " ;
return ERR_PARSE_ERROR ;
}
2014-02-09 22:10:30 -03:00
} break ;
2022-09-20 18:28:01 +02:00
case ' " ' :
case ' \\ ' :
case ' / ' : {
2015-11-16 17:05:39 +02:00
res = next ;
2014-02-09 22:10:30 -03:00
} break ;
2022-09-20 18:28:01 +02:00
default : {
r_err_str = " Invalid escape sequence. " ;
return ERR_PARSE_ERROR ;
}
2014-02-09 22:10:30 -03:00
}
str + = res ;
} else {
2020-05-14 16:41:43 +02:00
if ( p_str [ index ] = = ' \n ' ) {
2014-02-09 22:10:30 -03:00
line + + ;
2020-05-14 16:41:43 +02:00
}
2017-08-11 15:10:05 -04:00
str + = p_str [ index ] ;
2014-02-09 22:10:30 -03:00
}
2017-08-11 15:10:05 -04:00
index + + ;
2014-02-09 22:10:30 -03:00
}
r_token . type = TK_STRING ;
r_token . value = str ;
return OK ;
} break ;
default : {
2017-08-11 15:10:05 -04:00
if ( p_str [ index ] < = 32 ) {
index + + ;
2014-02-09 22:10:30 -03:00
break ;
}
2022-02-04 10:32:20 +02:00
if ( p_str [ index ] = = ' - ' | | is_digit ( p_str [ index ] ) ) {
2014-02-09 22:10:30 -03:00
//a number
2020-07-27 13:43:20 +03:00
const char32_t * rptr ;
2020-07-24 14:07:57 -04:00
double number = String : : to_float ( & p_str [ index ] , & rptr ) ;
2017-08-11 15:10:05 -04:00
index + = ( rptr - & p_str [ index ] ) ;
2014-02-09 22:10:30 -03:00
r_token . type = TK_NUMBER ;
r_token . value = number ;
return OK ;
2022-02-04 10:32:20 +02:00
} else if ( is_ascii_char ( p_str [ index ] ) ) {
2014-02-09 22:10:30 -03:00
String id ;
2022-02-04 10:32:20 +02:00
while ( is_ascii_char ( p_str [ index ] ) ) {
2017-08-11 15:10:05 -04:00
id + = p_str [ index ] ;
index + + ;
2014-02-09 22:10:30 -03:00
}
r_token . type = TK_IDENTIFIER ;
r_token . value = id ;
return OK ;
} else {
r_err_str = " Unexpected character. " ;
return ERR_PARSE_ERROR ;
}
}
}
}
return ERR_PARSE_ERROR ;
}
2022-09-19 18:10:23 +02:00
Error JSON : : _parse_value ( Variant & value , Token & token , const char32_t * p_str , int & index , int p_len , int & line , int p_depth , String & r_err_str ) {
if ( p_depth > Variant : : MAX_RECURSION_DEPTH ) {
r_err_str = " JSON structure is too deep. Bailing. " ;
return ERR_OUT_OF_MEMORY ;
}
2014-02-09 22:10:30 -03:00
if ( token . type = = TK_CURLY_BRACKET_OPEN ) {
2017-01-11 08:53:31 -03:00
Dictionary d ;
2022-09-19 18:10:23 +02:00
Error err = _parse_object ( d , p_str , index , p_len , line , p_depth + 1 , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
value = d ;
} else if ( token . type = = TK_BRACKET_OPEN ) {
2017-01-11 08:53:31 -03:00
Array a ;
2022-09-19 18:10:23 +02:00
Error err = _parse_array ( a , p_str , index , p_len , line , p_depth + 1 , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
value = a ;
} else if ( token . type = = TK_IDENTIFIER ) {
String id = token . value ;
2020-05-14 16:41:43 +02:00
if ( id = = " true " ) {
2014-02-09 22:10:30 -03:00
value = true ;
2020-05-14 16:41:43 +02:00
} else if ( id = = " false " ) {
2014-02-09 22:10:30 -03:00
value = false ;
2020-05-14 16:41:43 +02:00
} else if ( id = = " null " ) {
2014-02-09 22:10:30 -03:00
value = Variant ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-09 22:10:30 -03:00
r_err_str = " Expected 'true','false' or 'null', got ' " + id + " '. " ;
return ERR_PARSE_ERROR ;
}
} else if ( token . type = = TK_NUMBER ) {
value = token . value ;
} else if ( token . type = = TK_STRING ) {
value = token . value ;
} else {
r_err_str = " Expected value, got " + String ( tk_name [ token . type ] ) + " . " ;
return ERR_PARSE_ERROR ;
}
2020-07-28 21:09:01 +03:00
return OK ;
2014-02-09 22:10:30 -03:00
}
2022-09-19 18:10:23 +02:00
Error JSON : : _parse_array ( Array & array , const char32_t * p_str , int & index , int p_len , int & line , int p_depth , String & r_err_str ) {
2014-02-09 22:10:30 -03:00
Token token ;
bool need_comma = false ;
while ( index < p_len ) {
Error err = _get_token ( p_str , index , p_len , token , line , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
if ( token . type = = TK_BRACKET_CLOSE ) {
return OK ;
}
if ( need_comma ) {
if ( token . type ! = TK_COMMA ) {
r_err_str = " Expected ',' " ;
return ERR_PARSE_ERROR ;
} else {
need_comma = false ;
continue ;
}
}
Variant v ;
2022-09-19 18:10:23 +02:00
err = _parse_value ( v , token , p_str , index , p_len , line , p_depth , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
array . push_back ( v ) ;
need_comma = true ;
}
2020-07-22 15:06:57 -04:00
r_err_str = " Expected ']' " ;
2017-02-06 19:34:34 +00:00
return ERR_PARSE_ERROR ;
2014-02-09 22:10:30 -03:00
}
2022-09-19 18:10:23 +02:00
Error JSON : : _parse_object ( Dictionary & object , const char32_t * p_str , int & index , int p_len , int & line , int p_depth , String & r_err_str ) {
2014-02-09 22:10:30 -03:00
bool at_key = true ;
String key ;
Token token ;
bool need_comma = false ;
while ( index < p_len ) {
if ( at_key ) {
Error err = _get_token ( p_str , index , p_len , token , line , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
if ( token . type = = TK_CURLY_BRACKET_CLOSE ) {
return OK ;
}
if ( need_comma ) {
if ( token . type ! = TK_COMMA ) {
r_err_str = " Expected '}' or ',' " ;
return ERR_PARSE_ERROR ;
} else {
need_comma = false ;
continue ;
}
}
if ( token . type ! = TK_STRING ) {
r_err_str = " Expected key " ;
return ERR_PARSE_ERROR ;
}
key = token . value ;
err = _get_token ( p_str , index , p_len , token , line , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
if ( token . type ! = TK_COLON ) {
r_err_str = " Expected ':' " ;
return ERR_PARSE_ERROR ;
}
at_key = false ;
} else {
Error err = _get_token ( p_str , index , p_len , token , line , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ! = OK ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
Variant v ;
2022-09-19 18:10:23 +02:00
err = _parse_value ( v , token , p_str , index , p_len , line , p_depth , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
object [ key ] = v ;
need_comma = true ;
at_key = true ;
}
}
2020-07-22 15:06:57 -04:00
r_err_str = " Expected '}' " ;
2017-02-06 19:34:34 +00:00
return ERR_PARSE_ERROR ;
2014-02-09 22:10:30 -03:00
}
2022-09-03 19:45:24 +02:00
void JSON : : set_data ( const Variant & p_data ) {
data = p_data ;
2023-01-14 12:57:28 +01:00
text . clear ( ) ;
2022-09-03 19:45:24 +02:00
}
2020-12-29 18:12:33 +00:00
Error JSON : : _parse_string ( const String & p_json , Variant & r_ret , String & r_err_str , int & r_err_line ) {
2020-07-27 13:43:20 +03:00
const char32_t * str = p_json . ptr ( ) ;
2014-02-09 22:10:30 -03:00
int idx = 0 ;
int len = p_json . length ( ) ;
Token token ;
2017-01-08 22:40:00 -03:00
r_err_line = 0 ;
2014-02-09 22:10:30 -03:00
String aux_key ;
2017-01-08 22:40:00 -03:00
Error err = _get_token ( str , idx , len , token , r_err_line , r_err_str ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-09 22:10:30 -03:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2022-09-19 18:10:23 +02:00
err = _parse_value ( r_ret , token , str , idx , len , r_err_line , 0 , r_err_str ) ;
2014-02-09 22:10:30 -03:00
2020-07-28 21:09:01 +03:00
// Check if EOF is reached
// or it's a type of the next token.
if ( err = = OK & & idx < len ) {
err = _get_token ( str , idx , len , token , r_err_line , r_err_str ) ;
if ( err | | token . type ! = TK_EOF ) {
r_err_str = " Expected 'EOF' " ;
// Reset return value to empty `Variant`
r_ret = Variant ( ) ;
return ERR_PARSE_ERROR ;
}
}
2017-01-08 22:40:00 -03:00
return err ;
2014-02-09 22:10:30 -03:00
}
2020-11-10 18:31:33 -03:00
2023-01-14 12:57:28 +01:00
Error JSON : : parse ( const String & p_json_string , bool p_keep_text ) {
2020-12-29 18:12:33 +00:00
Error err = _parse_string ( p_json_string , data , err_str , err_line ) ;
if ( err = = Error : : OK ) {
err_line = 0 ;
}
2023-01-14 12:57:28 +01:00
if ( p_keep_text ) {
text = p_json_string ;
}
2020-12-29 18:12:33 +00:00
return err ;
2020-11-10 18:31:33 -03:00
}
2023-01-14 12:57:28 +01:00
String JSON : : get_parsed_text ( ) const {
return text ;
}
2022-04-25 17:23:39 +02:00
String JSON : : stringify ( const Variant & p_var , const String & p_indent , bool p_sort_keys , bool p_full_precision ) {
Ref < JSON > jason ;
jason . instantiate ( ) ;
HashSet < const void * > markers ;
return jason - > _stringify ( p_var , p_indent , 0 , p_sort_keys , markers , p_full_precision ) ;
}
Variant JSON : : parse_string ( const String & p_json_string ) {
Ref < JSON > jason ;
jason . instantiate ( ) ;
Error error = jason - > parse ( p_json_string ) ;
ERR_FAIL_COND_V_MSG ( error ! = Error : : OK , Variant ( ) , vformat ( " Parse JSON failed. Error at line %d: %s " , jason - > get_error_line ( ) , jason - > get_error_message ( ) ) ) ;
return jason - > get_data ( ) ;
}
2020-12-29 18:12:33 +00:00
void JSON : : _bind_methods ( ) {
2022-04-25 17:23:39 +02:00
ClassDB : : bind_static_method ( " JSON " , D_METHOD ( " stringify " , " data " , " indent " , " sort_keys " , " full_precision " ) , & JSON : : stringify , DEFVAL ( " " ) , DEFVAL ( true ) , DEFVAL ( false ) ) ;
ClassDB : : bind_static_method ( " JSON " , D_METHOD ( " parse_string " , " json_string " ) , & JSON : : parse_string ) ;
2023-01-14 12:57:28 +01:00
ClassDB : : bind_method ( D_METHOD ( " parse " , " json_text " , " keep_text " ) , & JSON : : parse , DEFVAL ( false ) ) ;
2020-11-10 18:31:33 -03:00
2020-12-29 18:12:33 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_data " ) , & JSON : : get_data ) ;
2022-09-03 19:45:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_data " , " data " ) , & JSON : : set_data ) ;
2023-01-14 12:57:28 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_parsed_text " ) , & JSON : : get_parsed_text ) ;
2020-12-29 18:12:33 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_error_line " ) , & JSON : : get_error_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_error_message " ) , & JSON : : get_error_message ) ;
2022-09-03 19:45:24 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : NIL , " data " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT ) , " set_data " , " get_data " ) ; // Ensures that it can be serialized as binary.
}
////
////////////
Ref < Resource > ResourceFormatLoaderJSON : : load ( const String & p_path , const String & p_original_path , Error * r_error , bool p_use_sub_threads , float * r_progress , CacheMode p_cache_mode ) {
if ( r_error ) {
* r_error = ERR_FILE_CANT_OPEN ;
}
if ( ! FileAccess : : exists ( p_path ) ) {
* r_error = ERR_FILE_NOT_FOUND ;
return Ref < Resource > ( ) ;
}
Ref < JSON > json ;
json . instantiate ( ) ;
2023-01-14 12:57:28 +01:00
Error err = json - > parse ( FileAccess : : get_file_as_string ( p_path ) , Engine : : get_singleton ( ) - > is_editor_hint ( ) ) ;
2022-09-03 19:45:24 +02:00
if ( err ! = OK ) {
2023-01-14 12:57:28 +01:00
String err_text = " Error parsing JSON file at ' " + p_path + " ', on line " + itos ( json - > get_error_line ( ) ) + " : " + json - > get_error_message ( ) ;
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
// If running on editor, still allow opening the JSON so the code editor can edit it.
WARN_PRINT ( err_text ) ;
} else {
if ( r_error ) {
* r_error = err ;
}
ERR_PRINT ( err_text ) ;
return Ref < Resource > ( ) ;
2022-09-03 19:45:24 +02:00
}
}
if ( r_error ) {
* r_error = OK ;
}
return json ;
}
void ResourceFormatLoaderJSON : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " json " ) ;
}
bool ResourceFormatLoaderJSON : : handles_type ( const String & p_type ) const {
return ( p_type = = " JSON " ) ;
}
String ResourceFormatLoaderJSON : : get_resource_type ( const String & p_path ) const {
String el = p_path . get_extension ( ) . to_lower ( ) ;
if ( el = = " json " ) {
return " JSON " ;
}
return " " ;
}
Error ResourceFormatSaverJSON : : save ( const Ref < Resource > & p_resource , const String & p_path , uint32_t p_flags ) {
Ref < JSON > json = p_resource ;
ERR_FAIL_COND_V ( json . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2023-01-14 12:57:28 +01:00
String source = json - > get_parsed_text ( ) . is_empty ( ) ? JSON : : stringify ( json - > get_data ( ) , " \t " , false , true ) : json - > get_parsed_text ( ) ;
2022-09-03 19:45:24 +02:00
Error err ;
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
ERR_FAIL_COND_V_MSG ( err , err , " Cannot save json ' " + p_path + " '. " ) ;
file - > store_string ( source ) ;
if ( file - > get_error ( ) ! = OK & & file - > get_error ( ) ! = ERR_FILE_EOF ) {
return ERR_CANT_CREATE ;
}
return OK ;
}
void ResourceFormatSaverJSON : : get_recognized_extensions ( const Ref < Resource > & p_resource , List < String > * p_extensions ) const {
Ref < JSON > json = p_resource ;
if ( json . is_valid ( ) ) {
p_extensions - > push_back ( " json " ) ;
}
}
bool ResourceFormatSaverJSON : : recognize ( const Ref < Resource > & p_resource ) const {
return p_resource - > get_class_name ( ) = = " JSON " ; //only json, not inherited
2020-11-10 18:31:33 -03:00
}