godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.71k stars 21.12k forks source link

JSON parser parses complex built-in types as arrays #65291

Open albinaask opened 2 years ago

albinaask commented 2 years ago

Godot version

3.5, 4.0?

System information

Windows 10, GLES 3, 1070ti, AMD Ryzen 7 2700x

Issue description

Parsing a variable to JSON with JSON.print([VAR]) and then converting the JSON back to a Variant with JSON.parse([JSONSTRING]) you would expect the same type in return or an error message telling you that the method has failed. You would not expect a string containing "0.9,1,1,1" for a 'Color' and the same string from a 'Plane'. Neither would you expect "[Resoruce:xxxxx]" for a resource, a float back when passing an int and so on.

Even though you could technically parse most of these things rather simply with just with for example "Color([RESULT])". You would however then expect a String as return in the method signature and some clue as to what type to parse it back to. And in that case the method would be rather pointless since it would just have trimmed the curly brackets.

The way to solve this would in my view be to express the types in the JSON format, something like the following:

"array" : {
    "String" : "godot"
    "int" : 1
    "float" : 1
    "Color": "Color(0.9,1,1,1)"//or whatever
    "Resource": [Resource:1245]
}

this would also have the benefit that the JSON format would be much more readable than it currently is, as it is currently looking like the following while which is a random Dictionary I used:

{"bool setting 1":true,"bool setting 2":false,"bool setting 3":false,"float setting":0,"float setting 2":1,"int setting 1":8,"color setting 1":"0.94,1,1,1","multi choice 1":"one","vec3 setting 1":"(1, 2, 3)","vec2 setting 1":"(0, 0)"}

Steps to reproduce

  1. Copy code from below to a test script in an existing project
  2. run the script in the editor.
  3. Observe that what comes back isn't what you'd think, which is being logged in the editor console

Minimal reproduction project

tool
extends EditorScript

func _run():
    var j
    var JPR

    #Strings work
    j = JSON.print("godot")
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("is string: " + str(typeof(JPR.result) == TYPE_STRING))
    print("//")

    #floats work
    j = JSON.print(1.0)
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("is float: " + str(typeof(JPR.result) == TYPE_REAL))
    print("//")

    #arrays work
    j = JSON.print([1,2,3])
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("is array: " + str(typeof(JPR.result) == TYPE_ARRAY))
    print("//")

    #Dictionaries don't work
    j = JSON.print(Color())
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("is dictionary: " + str(typeof(JPR.result) == TYPE_DICTIONARY))
    print("//")

    #ints don't work
    j = JSON.print(1)
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("is int: " + str(typeof(JPR.result) == TYPE_INT))
    print("//")

    #complex built-in types don't work
    j = JSON.print(Color())
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("Complex built-ins are broken: " + str(typeof(JPR.result) == TYPE_STRING))
    print("//")

    #Objects don't work
    j = JSON.print(load("res://icon.png"))
    print(j)
    JPR = JSON.parse(j)
    print(JPR.result)
    print("Complex built-ins are broken: " + str(typeof(JPR.result) == TYPE_STRING))
    print("//")
Zireael07 commented 2 years ago

For your use case, use var_to_str and str_to_var (or var2str and str2var in older parlance)

albinaask commented 2 years ago

Well, my use case is to store a Dictionary of data as a JSON file. This was just a demonstration of the problem.

timothyqiu commented 2 years ago
#Dictionaries don't work
j = JSON.print(Color())

Dictionary works. The Color here is a typo I think :)

#ints don't work
j = JSON.print(1)

This is explained in the documentation. JSON only has a "number" type, so it's not possible to distinguish between ints and floats.

Some JSON implementations use the .0 postfix to mark floats so those without the decimal point are turned into ints. But there will still be such problem when parsing foreign JSON data. e.g. The response from web APIs.

And other program / web services won't be able to understand Godot-specific representations even if it gets serialized into something like {"color": "Color(1, 1, 1, 0)"}.

albinaask commented 2 years ago

I think the issue here was that I misconceived the purpose of the parser in the way that it is a generic JSON parser rather than a way to serialize Godot data. This makes it more of a flaw regarding documentation or perhaps more likely of my brain skills rather than with the engine. So I'll close the issue here with the question whether we should perhaps add something in the intro to the JSON class documentation that it isn't meant for use in a serializing sense and that var2str or var_to_str is more suitable?

Calinou commented 2 years ago

Reopening, as the above needs to be documented.