You've already forked godot
							
							
				mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-04 12:00:25 +00:00 
			
		
		
		
	Handle NaN and Infinity in JSON stringify function
Co-authored-by: Thaddeus Crews <repiteo@outlook.com> Co-authored-by: Lukas Tenbrink <lukas.tenbrink@gmail.com>
This commit is contained in:
		@@ -76,6 +76,18 @@ void JSON::_stringify(String &r_result, const Variant &p_var, const String &p_in
 | 
			
		||||
		case Variant::FLOAT: {
 | 
			
		||||
			const double num = p_var;
 | 
			
		||||
 | 
			
		||||
			// JSON does not support NaN or Infinity, so use extremely large numbers for infinity.
 | 
			
		||||
			if (!Math::is_finite(num)) {
 | 
			
		||||
				if (num == Math::INF) {
 | 
			
		||||
					r_result += "1e99999";
 | 
			
		||||
				} else if (num == -Math::INF) {
 | 
			
		||||
					r_result += "-1e99999";
 | 
			
		||||
				} else {
 | 
			
		||||
					WARN_PRINT_ONCE("`NaN` (\"Not a Number\") found in argument passed to JSON.stringify(). `NaN` cannot be represented in JSON, so the value has been replaced with `null`. This warning will not be printed for any later NaN occurrences.");
 | 
			
		||||
					r_result += "null";
 | 
			
		||||
				}
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			// Only for exactly 0. If we have approximately 0 let the user decide how much
 | 
			
		||||
			// precision they want.
 | 
			
		||||
			if (num == double(0.0)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,7 @@
 | 
			
		||||
				[b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a Variant to JSON text will convert all numerical values to [float] types.
 | 
			
		||||
				[b]Note:[/b] If [param full_precision] is [code]true[/code], when stringifying floats, the unreliable digits are stringified in addition to the reliable digits to guarantee exact decoding.
 | 
			
		||||
				The [param indent] parameter controls if and how something is indented; its contents will be used where there should be an indent in the output. Even spaces like [code]"   "[/code] will work. [code]\t[/code] and [code]\n[/code] can also be used for a tab indent, or to make a newline for each indent respectively.
 | 
			
		||||
				[b]Warning:[/b] Non-finite numbers are not supported in JSON. Any occurrences of [constant @GDScript.INF] will be replaced with [code]1e99999[/code], and negative [constant @GDScript.INF] will be replaced with [code]-1e99999[/code], but they will be interpreted correctly as infinity by most JSON parsers. [constant @GDScript.NAN] will be replaced with [code]null[/code], and it will not be interpreted as NaN in JSON parsers. If you expect non-finite numbers, consider passing your data through [method from_native] first.
 | 
			
		||||
				[b]Example output:[/b]
 | 
			
		||||
				[codeblock]
 | 
			
		||||
				## JSON.stringify(my_dictionary)
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,18 @@ TEST_CASE("[JSON] Stringify arrays") {
 | 
			
		||||
	full_precision_array.push_back(0.12345678901234568);
 | 
			
		||||
	CHECK(JSON::stringify(full_precision_array, "", true, true) == "[0.12345678901234568]");
 | 
			
		||||
 | 
			
		||||
	Array non_finite_array;
 | 
			
		||||
	non_finite_array.push_back(Math::INF);
 | 
			
		||||
	non_finite_array.push_back(-Math::INF);
 | 
			
		||||
	non_finite_array.push_back(Math::NaN);
 | 
			
		||||
	ERR_PRINT_OFF
 | 
			
		||||
	CHECK(JSON::stringify(non_finite_array) == "[1e99999,-1e99999,null]");
 | 
			
		||||
 | 
			
		||||
	Array non_finite_round_trip = JSON::parse_string(JSON::stringify(non_finite_array));
 | 
			
		||||
	CHECK(non_finite_round_trip[0] == Variant(Math::INF));
 | 
			
		||||
	CHECK(non_finite_round_trip[1] == Variant(-Math::INF));
 | 
			
		||||
	CHECK(non_finite_round_trip[2].get_type() == Variant::NIL);
 | 
			
		||||
 | 
			
		||||
	Array self_array;
 | 
			
		||||
	self_array.push_back(self_array);
 | 
			
		||||
	CHECK(JSON::stringify(self_array) == "[\"[...]\"]");
 | 
			
		||||
@@ -113,7 +124,18 @@ TEST_CASE("[JSON] Stringify dictionaries") {
 | 
			
		||||
	full_precision_dictionary["key"] = 0.12345678901234568;
 | 
			
		||||
	CHECK(JSON::stringify(full_precision_dictionary, "", true, true) == "{\"key\":0.12345678901234568}");
 | 
			
		||||
 | 
			
		||||
	Dictionary non_finite_dictionary;
 | 
			
		||||
	non_finite_dictionary["-inf"] = -Math::INF;
 | 
			
		||||
	non_finite_dictionary["inf"] = Math::INF;
 | 
			
		||||
	non_finite_dictionary["nan"] = Math::NaN;
 | 
			
		||||
	ERR_PRINT_OFF
 | 
			
		||||
	CHECK(JSON::stringify(non_finite_dictionary) == "{\"-inf\":-1e99999,\"inf\":1e99999,\"nan\":null}");
 | 
			
		||||
 | 
			
		||||
	Dictionary non_finite_round_trip = JSON::parse_string(JSON::stringify(non_finite_dictionary));
 | 
			
		||||
	CHECK(non_finite_round_trip["-inf"] == Variant(-Math::INF));
 | 
			
		||||
	CHECK(non_finite_round_trip["inf"] == Variant(Math::INF));
 | 
			
		||||
	CHECK(non_finite_round_trip["nan"].get_type() == Variant::NIL);
 | 
			
		||||
 | 
			
		||||
	Dictionary self_dictionary;
 | 
			
		||||
	self_dictionary["key"] = self_dictionary;
 | 
			
		||||
	CHECK(JSON::stringify(self_dictionary) == "{\"key\":\"{...}\"}");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user