godot icon indicating copy to clipboard operation
godot copied to clipboard

Fix JSON Parsing to Differentiate Integers and Floats

Open Ebbo opened this issue 1 year ago • 3 comments

This change enhances JSON compatibility by correctly parsing numeric types, aligning the behavior with expectations for strict JSON type handling.

Data:

{
	"key1": 42,
	"key2": 3.14,
	"key3": 100,
	"key4": 541.,
	"key5": 0.0,
	"key6": 0102
}

Example GDScript:

func _ready() -> void:
	var data = FileAccess.get_file_as_string("res://data.json")

	var json_data = JSON.parse_string(data)
	print(json_data["key1"])
	print(json_data["key2"])
	print(json_data["key3"])
	print(json_data["key4"])
	print(json_data["key5"])
	print(json_data["key6"])

Current:
42.0
3.14
100.0
541.0
0.0
102.0
Patch:
42
3.14
100
541.0
0.0
102

Ebbo avatar Dec 15 '24 22:12 Ebbo

Thanks for the PR! You need to open a design proposal as well, since this is not a bug fix but rather a (breaking) change in behavior. It's especially important to know what problem you are trying to solve, since JSON itself does not distinguish numbers vs integers to begin with.

RedMser avatar Dec 15 '24 23:12 RedMser

Thanks for the information! I will have a look at that - I simply realized that parsing json data behaves differently between 4.3 to 4.4. If eg. 100 was parsed it was also used as integer instead of double. Now this behavior changed. That's why I wanted to provide this change.

Ebbo avatar Dec 15 '24 23:12 Ebbo

I simply realized that parsing json data behaves differently between 4.3 to 4.4.

Ah gotcha! 4.4 made it so stringifying floats always appends the .0 now, instead of only doing so whenever there's an actual decimal part. This also applies to print(). So I believe it was always parsing floats, but it is made more visible now.

RedMser avatar Dec 15 '24 23:12 RedMser

{ "key1": 42, "key2": 3.14, "key3": 100, "key4": 541., "key5": 0.0, "key6": 0102 }

This isn't valid JSON, so it's not a proper example, but actually validating it will also show why .0 can't be relied on.

Try to input it in https://jsonlint.com/

"key4": 541.,

This is invalid, JSON doesn't support 541. syntax.

"key6": 0102

This also appears to be invalid, leading zeroes are not supported.

After fixing those two issues, you'll see that jsonlint.com will clean it to:

{
    "key1": 42,
    "key2": 3.14,
    "key3": 100,
    "key4": 541,
    "key5": 0,
    "key6": 102
}

Other tools such as https://codebeautify.org/jsonminifier will minify it to:

{"key1":42,"key2":3.14,"key3":100,"key4":541,"key5":0,"key6":102}

I.e. no trailing .0 for floats.

That's because the JSON specification only supports Number, and doesn't make a distinction between floats and integers. For JSON, 1.0 and 1 are the same Number and the .0 is irrelevant information.

So you can't expect it to be significant and preserve and use it for serialization with Godot, or any engine that needs separate integers and floats.

The change you noticed in 4.4 is that we now always print whole floats with .0 to make it clear they're floats. They were still floats in 4.3, just not printed explicitly as such.

If you want to preserve Godot type information in JSON, the recommended way would be to use the new JSON.from_native and JSON.to_native methods, which encode all Godot types in a way that can be parsed back to the original Variant.

akien-mga avatar Dec 16 '24 08:12 akien-mga