2
0
Эх сурвалжийг харах

Merge pull request #99765 from dalexeev/core-fix-json-from-to-native

Core: Fix `JSON.{from,to}_native()` issues
Thaddeus Crews 7 сар өмнө
parent
commit
8f16f864a6

+ 812 - 552
core/io/json.cpp

@@ -31,7 +31,8 @@
 #include "json.h"
 #include "json.h"
 
 
 #include "core/config/engine.h"
 #include "core/config/engine.h"
-#include "core/string/print_string.h"
+#include "core/object/script_language.h"
+#include "core/variant/container_type_validate.h"
 
 
 const char *JSON::tk_name[TK_MAX] = {
 const char *JSON::tk_name[TK_MAX] = {
 	"'{'",
 	"'{'",
@@ -563,18 +564,18 @@ String JSON::get_parsed_text() const {
 }
 }
 
 
 String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) {
 String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) {
-	Ref<JSON> jason;
-	jason.instantiate();
+	Ref<JSON> json;
+	json.instantiate();
 	HashSet<const void *> markers;
 	HashSet<const void *> markers;
-	return jason->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision);
+	return json->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision);
 }
 }
 
 
 Variant JSON::parse_string(const String &p_json_string) {
 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();
+	Ref<JSON> json;
+	json.instantiate();
+	Error error = json->parse(p_json_string);
+	ERR_FAIL_COND_V_MSG(error != Error::OK, Variant(), vformat("Parse JSON failed. Error at line %d: %s", json->get_error_line(), json->get_error_message()));
+	return json->get_data();
 }
 }
 
 
 void JSON::_bind_methods() {
 void JSON::_bind_methods() {
@@ -588,756 +589,1015 @@ void JSON::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
 	ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
 	ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
 	ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
 
 
-	ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false));
-	ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false));
+	ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "full_objects"), &JSON::from_native, DEFVAL(false));
+	ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_objects"), &JSON::to_native, DEFVAL(false));
 
 
 	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.
 	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.
 }
 }
 
 
-#define GDTYPE "__gdtype"
-#define VALUES "values"
-#define PASS_ARG p_allow_classes, p_allow_scripts
+#define TYPE "type"
+#define ELEM_TYPE "elem_type"
+#define KEY_TYPE "key_type"
+#define VALUE_TYPE "value_type"
+#define ARGS "args"
+#define PROPS "props"
+
+static bool _encode_container_type(Dictionary &r_dict, const String &p_key, const ContainerType &p_type, bool p_full_objects) {
+	if (p_type.builtin_type != Variant::NIL) {
+		if (p_type.script.is_valid()) {
+			ERR_FAIL_COND_V(!p_full_objects, false);
+			const String path = p_type.script->get_path();
+			ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), false, "Failed to encode a path to a custom script for a container type.");
+			r_dict[p_key] = path;
+		} else if (p_type.class_name != StringName()) {
+			ERR_FAIL_COND_V(!p_full_objects, false);
+			r_dict[p_key] = String(p_type.class_name);
+		} else {
+			// No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
+			r_dict[p_key] = Variant::get_type_name(p_type.builtin_type);
+		}
+	}
+	return true;
+}
+
+Variant JSON::_from_native(const Variant &p_variant, bool p_full_objects, int p_depth) {
+#define RETURN_ARGS                                           \
+	Dictionary ret;                                           \
+	ret[TYPE] = Variant::get_type_name(p_variant.get_type()); \
+	ret[ARGS] = args;                                         \
+	return ret
 
 
-Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) {
 	switch (p_variant.get_type()) {
 	switch (p_variant.get_type()) {
-		case Variant::NIL: {
-			Dictionary nil;
-			nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return nil;
-		} break;
+		case Variant::NIL:
 		case Variant::BOOL: {
 		case Variant::BOOL: {
 			return p_variant;
 			return p_variant;
 		} break;
 		} break;
+
 		case Variant::INT: {
 		case Variant::INT: {
-			return p_variant;
+			return "i:" + String(p_variant);
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
-			return p_variant;
+			return "f:" + String(p_variant);
 		} break;
 		} break;
 		case Variant::STRING: {
 		case Variant::STRING: {
-			return p_variant;
+			return "s:" + String(p_variant);
+		} break;
+		case Variant::STRING_NAME: {
+			return "sn:" + String(p_variant);
+		} break;
+		case Variant::NODE_PATH: {
+			return "np:" + String(p_variant);
+		} break;
+
+		case Variant::RID:
+		case Variant::CALLABLE:
+		case Variant::SIGNAL: {
+			Dictionary ret;
+			ret[TYPE] = Variant::get_type_name(p_variant.get_type());
+			return ret;
 		} break;
 		} break;
+
 		case Variant::VECTOR2: {
 		case Variant::VECTOR2: {
-			Dictionary d;
-			Vector2 v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector2 v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::VECTOR2I: {
 		case Variant::VECTOR2I: {
-			Dictionary d;
-			Vector2i v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector2i v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::RECT2: {
 		case Variant::RECT2: {
-			Dictionary d;
-			Rect2 r = p_variant;
-			d["position"] = from_native(r.position);
-			d["size"] = from_native(r.size);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Rect2 r = p_variant;
+
+			Array args;
+			args.push_back(r.position.x);
+			args.push_back(r.position.y);
+			args.push_back(r.size.width);
+			args.push_back(r.size.height);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::RECT2I: {
 		case Variant::RECT2I: {
-			Dictionary d;
-			Rect2i r = p_variant;
-			d["position"] = from_native(r.position);
-			d["size"] = from_native(r.size);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Rect2i r = p_variant;
+
+			Array args;
+			args.push_back(r.position.x);
+			args.push_back(r.position.y);
+			args.push_back(r.size.width);
+			args.push_back(r.size.height);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::VECTOR3: {
 		case Variant::VECTOR3: {
-			Dictionary d;
-			Vector3 v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			values.push_back(v.z);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector3 v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+			args.push_back(v.z);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::VECTOR3I: {
 		case Variant::VECTOR3I: {
-			Dictionary d;
-			Vector3i v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			values.push_back(v.z);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector3i v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+			args.push_back(v.z);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::TRANSFORM2D: {
 		case Variant::TRANSFORM2D: {
-			Dictionary d;
-			Transform2D t = p_variant;
-			d["x"] = from_native(t[0]);
-			d["y"] = from_native(t[1]);
-			d["origin"] = from_native(t[2]);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Transform2D t = p_variant;
+
+			Array args;
+			args.push_back(t[0].x);
+			args.push_back(t[0].y);
+			args.push_back(t[1].x);
+			args.push_back(t[1].y);
+			args.push_back(t[2].x);
+			args.push_back(t[2].y);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::VECTOR4: {
 		case Variant::VECTOR4: {
-			Dictionary d;
-			Vector4 v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			values.push_back(v.z);
-			values.push_back(v.w);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector4 v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+			args.push_back(v.z);
+			args.push_back(v.w);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::VECTOR4I: {
 		case Variant::VECTOR4I: {
-			Dictionary d;
-			Vector4i v = p_variant;
-			Array values;
-			values.push_back(v.x);
-			values.push_back(v.y);
-			values.push_back(v.z);
-			values.push_back(v.w);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Vector4i v = p_variant;
+
+			Array args;
+			args.push_back(v.x);
+			args.push_back(v.y);
+			args.push_back(v.z);
+			args.push_back(v.w);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PLANE: {
 		case Variant::PLANE: {
-			Dictionary d;
-			Plane p = p_variant;
-			d["normal"] = from_native(p.normal);
-			d["d"] = p.d;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Plane p = p_variant;
+
+			Array args;
+			args.push_back(p.normal.x);
+			args.push_back(p.normal.y);
+			args.push_back(p.normal.z);
+			args.push_back(p.d);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::QUATERNION: {
 		case Variant::QUATERNION: {
-			Dictionary d;
-			Quaternion q = p_variant;
-			Array values;
-			values.push_back(q.x);
-			values.push_back(q.y);
-			values.push_back(q.z);
-			values.push_back(q.w);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Quaternion q = p_variant;
+
+			Array args;
+			args.push_back(q.x);
+			args.push_back(q.y);
+			args.push_back(q.z);
+			args.push_back(q.w);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::AABB: {
 		case Variant::AABB: {
-			Dictionary d;
-			AABB aabb = p_variant;
-			d["position"] = from_native(aabb.position);
-			d["size"] = from_native(aabb.size);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const AABB aabb = p_variant;
+
+			Array args;
+			args.push_back(aabb.position.x);
+			args.push_back(aabb.position.y);
+			args.push_back(aabb.position.z);
+			args.push_back(aabb.size.x);
+			args.push_back(aabb.size.y);
+			args.push_back(aabb.size.z);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::BASIS: {
 		case Variant::BASIS: {
-			Dictionary d;
-			Basis t = p_variant;
-			d["x"] = from_native(t.get_column(0));
-			d["y"] = from_native(t.get_column(1));
-			d["z"] = from_native(t.get_column(2));
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Basis b = p_variant;
+
+			Array args;
+			args.push_back(b.get_column(0).x);
+			args.push_back(b.get_column(0).y);
+			args.push_back(b.get_column(0).z);
+			args.push_back(b.get_column(1).x);
+			args.push_back(b.get_column(1).y);
+			args.push_back(b.get_column(1).z);
+			args.push_back(b.get_column(2).x);
+			args.push_back(b.get_column(2).y);
+			args.push_back(b.get_column(2).z);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::TRANSFORM3D: {
 		case Variant::TRANSFORM3D: {
-			Dictionary d;
-			Transform3D t = p_variant;
-			d["basis"] = from_native(t.basis);
-			d["origin"] = from_native(t.origin);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Transform3D t = p_variant;
+
+			Array args;
+			args.push_back(t.basis.get_column(0).x);
+			args.push_back(t.basis.get_column(0).y);
+			args.push_back(t.basis.get_column(0).z);
+			args.push_back(t.basis.get_column(1).x);
+			args.push_back(t.basis.get_column(1).y);
+			args.push_back(t.basis.get_column(1).z);
+			args.push_back(t.basis.get_column(2).x);
+			args.push_back(t.basis.get_column(2).y);
+			args.push_back(t.basis.get_column(2).z);
+			args.push_back(t.origin.x);
+			args.push_back(t.origin.y);
+			args.push_back(t.origin.z);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PROJECTION: {
 		case Variant::PROJECTION: {
-			Dictionary d;
-			Projection t = p_variant;
-			d["x"] = from_native(t[0]);
-			d["y"] = from_native(t[1]);
-			d["z"] = from_native(t[2]);
-			d["w"] = from_native(t[3]);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Projection p = p_variant;
+
+			Array args;
+			args.push_back(p[0].x);
+			args.push_back(p[0].y);
+			args.push_back(p[0].z);
+			args.push_back(p[0].w);
+			args.push_back(p[1].x);
+			args.push_back(p[1].y);
+			args.push_back(p[1].z);
+			args.push_back(p[1].w);
+			args.push_back(p[2].x);
+			args.push_back(p[2].y);
+			args.push_back(p[2].z);
+			args.push_back(p[2].w);
+			args.push_back(p[3].x);
+			args.push_back(p[3].y);
+			args.push_back(p[3].z);
+			args.push_back(p[3].w);
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::COLOR: {
 		case Variant::COLOR: {
-			Dictionary d;
-			Color c = p_variant;
-			Array values;
-			values.push_back(c.r);
-			values.push_back(c.g);
-			values.push_back(c.b);
-			values.push_back(c.a);
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
-		} break;
-		case Variant::STRING_NAME: {
-			Dictionary d;
-			d["name"] = String(p_variant);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
-		} break;
-		case Variant::NODE_PATH: {
-			Dictionary d;
-			d["path"] = String(p_variant);
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
-		} break;
-		case Variant::RID: {
-			Dictionary d;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+			const Color c = p_variant;
+
+			Array args;
+			args.push_back(c.r);
+			args.push_back(c.g);
+			args.push_back(c.b);
+			args.push_back(c.a);
+
+			RETURN_ARGS;
 		} break;
 		} break;
+
 		case Variant::OBJECT: {
 		case Variant::OBJECT: {
-			Object *obj = p_variant.get_validated_object();
+			ERR_FAIL_COND_V(!p_full_objects, Variant());
 
 
-			if (p_allow_classes && obj) {
-				Dictionary d;
-				List<PropertyInfo> property_list;
-				obj->get_property_list(&property_list);
-
-				d["type"] = obj->get_class();
-				Dictionary p;
-				for (const PropertyInfo &P : property_list) {
-					if (P.usage & PROPERTY_USAGE_STORAGE) {
-						if (P.name == "script" && !p_allow_scripts) {
-							continue;
-						}
-						p[P.name] = from_native(obj->get(P.name), PASS_ARG);
+			ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, Variant(), "Variant is too deep. Bailing.");
+
+			const Object *obj = p_variant.get_validated_object();
+			if (obj == nullptr) {
+				return Variant();
+			}
+
+			ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), Variant());
+
+			List<PropertyInfo> prop_list;
+			obj->get_property_list(&prop_list);
+
+			Array props;
+			for (const PropertyInfo &pi : prop_list) {
+				if (!(pi.usage & PROPERTY_USAGE_STORAGE)) {
+					continue;
+				}
+
+				Variant value;
+				if (pi.name == CoreStringName(script)) {
+					const Ref<Script> script = obj->get_script();
+					if (script.is_valid()) {
+						const String path = script->get_path();
+						ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), Variant(), "Failed to encode a path to a custom script.");
+						value = path;
 					}
 					}
+				} else {
+					value = obj->get(pi.name);
 				}
 				}
-				d["properties"] = p;
-				d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-				return d;
-			} else {
-				Dictionary nil;
-				nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-				return nil;
+
+				props.push_back(pi.name);
+				props.push_back(_from_native(value, p_full_objects, p_depth + 1));
 			}
 			}
+
+			Dictionary ret;
+			ret[TYPE] = obj->get_class();
+			ret[PROPS] = props;
+			return ret;
 		} break;
 		} break;
-		case Variant::CALLABLE:
-		case Variant::SIGNAL: {
-			Dictionary nil;
-			nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return nil;
-		} break;
+
 		case Variant::DICTIONARY: {
 		case Variant::DICTIONARY: {
-			Dictionary d = p_variant;
+			const Dictionary dict = p_variant;
+
+			Array args;
+
+			Dictionary ret;
+			ret[TYPE] = Variant::get_type_name(p_variant.get_type());
+			if (!_encode_container_type(ret, KEY_TYPE, dict.get_key_type(), p_full_objects)) {
+				return Variant();
+			}
+			if (!_encode_container_type(ret, VALUE_TYPE, dict.get_value_type(), p_full_objects)) {
+				return Variant();
+			}
+			ret[ARGS] = args;
+
+			ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ret, "Variant is too deep. Bailing.");
+
 			List<Variant> keys;
 			List<Variant> keys;
-			d.get_key_list(&keys);
-			bool all_strings = true;
-			for (const Variant &K : keys) {
-				if (K.get_type() != Variant::STRING) {
-					all_strings = false;
-					break;
-				}
+			dict.get_key_list(&keys);
+
+			for (const Variant &key : keys) {
+				args.push_back(_from_native(key, p_full_objects, p_depth + 1));
+				args.push_back(_from_native(dict[key], p_full_objects, p_depth + 1));
 			}
 			}
 
 
-			if (all_strings) {
-				Dictionary ret_dict;
-				for (const Variant &K : keys) {
-					ret_dict[K] = from_native(d[K], PASS_ARG);
+			return ret;
+		} break;
+
+		case Variant::ARRAY: {
+			const Array arr = p_variant;
+
+			Variant ret;
+			Array args;
+
+			if (arr.is_typed()) {
+				Dictionary d;
+				d[TYPE] = Variant::get_type_name(p_variant.get_type());
+				if (!_encode_container_type(d, ELEM_TYPE, arr.get_element_type(), p_full_objects)) {
+					return Variant();
 				}
 				}
-				return ret_dict;
+				d[ARGS] = args;
+				ret = d;
 			} else {
 			} else {
-				Dictionary ret;
-				Array pairs;
-				for (const Variant &K : keys) {
-					Dictionary pair;
-					pair["key"] = from_native(K, PASS_ARG);
-					pair["value"] = from_native(d[K], PASS_ARG);
-					pairs.push_back(pair);
-				}
-				ret["pairs"] = pairs;
-				ret[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-				return ret;
+				ret = args;
 			}
 			}
-		} break;
-		case Variant::ARRAY: {
-			Array arr = p_variant;
-			Array ret;
+
+			ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ret, "Variant is too deep. Bailing.");
+
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				ret.push_back(from_native(arr[i], PASS_ARG));
+				args.push_back(_from_native(arr[i], p_full_objects, p_depth + 1));
 			}
 			}
+
 			return ret;
 			return ret;
 		} break;
 		} break;
+
 		case Variant::PACKED_BYTE_ARRAY: {
 		case Variant::PACKED_BYTE_ARRAY: {
-			Dictionary d;
-			PackedByteArray arr = p_variant;
-			Array values;
+			const PackedByteArray arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_INT32_ARRAY: {
 		case Variant::PACKED_INT32_ARRAY: {
-			Dictionary d;
-			PackedInt32Array arr = p_variant;
-			Array values;
+			const PackedInt32Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
 
 
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_INT64_ARRAY: {
 		case Variant::PACKED_INT64_ARRAY: {
-			Dictionary d;
-			PackedInt64Array arr = p_variant;
-			Array values;
+			const PackedInt64Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_FLOAT32_ARRAY: {
 		case Variant::PACKED_FLOAT32_ARRAY: {
-			Dictionary d;
-			PackedFloat32Array arr = p_variant;
-			Array values;
+			const PackedFloat32Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_FLOAT64_ARRAY: {
 		case Variant::PACKED_FLOAT64_ARRAY: {
-			Dictionary d;
-			PackedFloat64Array arr = p_variant;
-			Array values;
+			const PackedFloat64Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_STRING_ARRAY: {
 		case Variant::PACKED_STRING_ARRAY: {
-			Dictionary d;
-			PackedStringArray arr = p_variant;
-			Array values;
+			const PackedStringArray arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				values.push_back(arr[i]);
+				args.push_back(arr[i]);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_VECTOR2_ARRAY: {
 		case Variant::PACKED_VECTOR2_ARRAY: {
-			Dictionary d;
-			PackedVector2Array arr = p_variant;
-			Array values;
+			const PackedVector2Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
 				Vector2 v = arr[i];
 				Vector2 v = arr[i];
-				values.push_back(v.x);
-				values.push_back(v.y);
+				args.push_back(v.x);
+				args.push_back(v.y);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_VECTOR3_ARRAY: {
 		case Variant::PACKED_VECTOR3_ARRAY: {
-			Dictionary d;
-			PackedVector3Array arr = p_variant;
-			Array values;
+			const PackedVector3Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
 				Vector3 v = arr[i];
 				Vector3 v = arr[i];
-				values.push_back(v.x);
-				values.push_back(v.y);
-				values.push_back(v.z);
+				args.push_back(v.x);
+				args.push_back(v.y);
+				args.push_back(v.z);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_COLOR_ARRAY: {
 		case Variant::PACKED_COLOR_ARRAY: {
-			Dictionary d;
-			PackedColorArray arr = p_variant;
-			Array values;
+			const PackedColorArray arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
 				Color v = arr[i];
 				Color v = arr[i];
-				values.push_back(v.r);
-				values.push_back(v.g);
-				values.push_back(v.b);
-				values.push_back(v.a);
+				args.push_back(v.r);
+				args.push_back(v.g);
+				args.push_back(v.b);
+				args.push_back(v.a);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
 		case Variant::PACKED_VECTOR4_ARRAY: {
 		case Variant::PACKED_VECTOR4_ARRAY: {
-			Dictionary d;
-			PackedVector4Array arr = p_variant;
-			Array values;
+			const PackedVector4Array arr = p_variant;
+
+			Array args;
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
 				Vector4 v = arr[i];
 				Vector4 v = arr[i];
-				values.push_back(v.x);
-				values.push_back(v.y);
-				values.push_back(v.z);
-				values.push_back(v.w);
+				args.push_back(v.x);
+				args.push_back(v.y);
+				args.push_back(v.z);
+				args.push_back(v.w);
 			}
 			}
-			d[VALUES] = values;
-			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-			return d;
+
+			RETURN_ARGS;
 		} break;
 		} break;
-		default: {
-			ERR_PRINT(vformat("Unhandled conversion from native Variant type '%s' to JSON.", Variant::get_type_name(p_variant.get_type())));
+
+		case Variant::VARIANT_MAX: {
+			// Nothing to do.
 		} break;
 		} break;
 	}
 	}
 
 
-	Dictionary nil;
-	nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
-	return nil;
+#undef RETURN_ARGS
+
+	ERR_FAIL_V_MSG(Variant(), vformat(R"(Unhandled Variant type "%s".)", Variant::get_type_name(p_variant.get_type())));
 }
 }
 
 
-Variant JSON::to_native(const Variant &p_json, bool p_allow_classes, bool p_allow_scripts) {
+static bool _decode_container_type(const Dictionary &p_dict, const String &p_key, ContainerType &r_type, bool p_allow_objects) {
+	if (!p_dict.has(p_key)) {
+		return true;
+	}
+
+	const String type_name = p_dict[p_key];
+
+	const Variant::Type builtin_type = Variant::get_type_by_name(type_name);
+	if (builtin_type < Variant::VARIANT_MAX && builtin_type != Variant::OBJECT) {
+		r_type.builtin_type = builtin_type;
+		return true;
+	}
+
+	if (ClassDB::class_exists(type_name)) {
+		ERR_FAIL_COND_V(!p_allow_objects, false);
+
+		r_type.builtin_type = Variant::OBJECT;
+		r_type.class_name = type_name;
+		return true;
+	}
+
+	if (type_name.begins_with("res://")) {
+		ERR_FAIL_COND_V(!p_allow_objects, false);
+
+		ERR_FAIL_COND_V_MSG(!ResourceLoader::exists(type_name, "Script"), false, vformat(R"(Invalid script path "%s".)", type_name));
+		const Ref<Script> script = ResourceLoader::load(type_name, "Script");
+		ERR_FAIL_COND_V_MSG(script.is_null(), false, vformat(R"(Can't load script at path "%s".)", type_name));
+
+		r_type.builtin_type = Variant::OBJECT;
+		r_type.class_name = script->get_instance_base_type();
+		r_type.script = script;
+		return true;
+	}
+
+	ERR_FAIL_V_MSG(false, vformat(R"(Invalid type "%s".)", type_name));
+}
+
+Variant JSON::_to_native(const Variant &p_json, bool p_allow_objects, int p_depth) {
 	switch (p_json.get_type()) {
 	switch (p_json.get_type()) {
+		case Variant::NIL:
 		case Variant::BOOL: {
 		case Variant::BOOL: {
 			return p_json;
 			return p_json;
 		} break;
 		} break;
-		case Variant::INT: {
-			return p_json;
-		} break;
-		case Variant::FLOAT: {
-			return p_json;
-		} break;
+
 		case Variant::STRING: {
 		case Variant::STRING: {
-			return p_json;
-		} break;
-		case Variant::STRING_NAME: {
-			return p_json;
-		} break;
-		case Variant::CALLABLE: {
-			return p_json;
+			const String s = p_json;
+
+			if (s.begins_with("i:")) {
+				return s.substr(2).to_int();
+			} else if (s.begins_with("f:")) {
+				return s.substr(2).to_float();
+			} else if (s.begins_with("s:")) {
+				return s.substr(2);
+			} else if (s.begins_with("sn:")) {
+				return StringName(s.substr(3));
+			} else if (s.begins_with("np:")) {
+				return NodePath(s.substr(3));
+			}
+
+			ERR_FAIL_V_MSG(Variant(), "Invalid string, the type prefix is not recognized.");
 		} break;
 		} break;
+
 		case Variant::DICTIONARY: {
 		case Variant::DICTIONARY: {
-			Dictionary d = p_json;
-			if (d.has(GDTYPE)) {
-				// Specific Godot Variant types serialized to JSON.
-				String type = d[GDTYPE];
-				if (type == Variant::get_type_name(Variant::VECTOR2)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 2, Variant());
+			const Dictionary dict = p_json;
+
+			ERR_FAIL_COND_V(!dict.has(TYPE), Variant());
+
+#define LOAD_ARGS()                              \
+	ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \
+	const Array args = dict[ARGS]
+
+#define LOAD_ARGS_CHECK_SIZE(m_size)             \
+	ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \
+	const Array args = dict[ARGS];               \
+	ERR_FAIL_COND_V(args.size() != (m_size), Variant())
+
+#define LOAD_ARGS_CHECK_FACTOR(m_factor)         \
+	ERR_FAIL_COND_V(!dict.has(ARGS), Variant()); \
+	const Array args = dict[ARGS];               \
+	ERR_FAIL_COND_V(args.size() % (m_factor) != 0, Variant())
+
+			switch (Variant::get_type_by_name(dict[TYPE])) {
+				case Variant::NIL:
+				case Variant::BOOL: {
+					ERR_FAIL_V_MSG(Variant(), vformat(R"(Unexpected "%s": Variant type "%s" is JSON-compliant.)", TYPE, dict[TYPE]));
+				} break;
+
+				case Variant::INT:
+				case Variant::FLOAT:
+				case Variant::STRING:
+				case Variant::STRING_NAME:
+				case Variant::NODE_PATH: {
+					ERR_FAIL_V_MSG(Variant(), vformat(R"(Unexpected "%s": Variant type "%s" must be represented as a string.)", TYPE, dict[TYPE]));
+				} break;
+
+				case Variant::RID: {
+					return RID();
+				} break;
+				case Variant::CALLABLE: {
+					return Callable();
+				} break;
+				case Variant::SIGNAL: {
+					return Signal();
+				} break;
+
+				case Variant::VECTOR2: {
+					LOAD_ARGS_CHECK_SIZE(2);
+
 					Vector2 v;
 					Vector2 v;
-					v.x = values[0];
-					v.y = values[1];
+					v.x = args[0];
+					v.y = args[1];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::VECTOR2I)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 2, Variant());
+				} break;
+				case Variant::VECTOR2I: {
+					LOAD_ARGS_CHECK_SIZE(2);
+
 					Vector2i v;
 					Vector2i v;
-					v.x = values[0];
-					v.y = values[1];
+					v.x = args[0];
+					v.y = args[1];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::RECT2)) {
-					ERR_FAIL_COND_V(!d.has("position"), Variant());
-					ERR_FAIL_COND_V(!d.has("size"), Variant());
+				} break;
+				case Variant::RECT2: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Rect2 r;
 					Rect2 r;
-					r.position = to_native(d["position"]);
-					r.size = to_native(d["size"]);
+					r.position = Point2(args[0], args[1]);
+					r.size = Size2(args[2], args[3]);
+
 					return r;
 					return r;
-				} else if (type == Variant::get_type_name(Variant::RECT2I)) {
-					ERR_FAIL_COND_V(!d.has("position"), Variant());
-					ERR_FAIL_COND_V(!d.has("size"), Variant());
+				} break;
+				case Variant::RECT2I: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Rect2i r;
 					Rect2i r;
-					r.position = to_native(d["position"]);
-					r.size = to_native(d["size"]);
+					r.position = Point2i(args[0], args[1]);
+					r.size = Size2i(args[2], args[3]);
+
 					return r;
 					return r;
-				} else if (type == Variant::get_type_name(Variant::VECTOR3)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 3, Variant());
+				} break;
+				case Variant::VECTOR3: {
+					LOAD_ARGS_CHECK_SIZE(3);
+
 					Vector3 v;
 					Vector3 v;
-					v.x = values[0];
-					v.y = values[1];
-					v.z = values[2];
+					v.x = args[0];
+					v.y = args[1];
+					v.z = args[2];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::VECTOR3I)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 3, Variant());
+				} break;
+				case Variant::VECTOR3I: {
+					LOAD_ARGS_CHECK_SIZE(3);
+
 					Vector3i v;
 					Vector3i v;
-					v.x = values[0];
-					v.y = values[1];
-					v.z = values[2];
+					v.x = args[0];
+					v.y = args[1];
+					v.z = args[2];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::TRANSFORM2D)) {
-					ERR_FAIL_COND_V(!d.has("x"), Variant());
-					ERR_FAIL_COND_V(!d.has("y"), Variant());
-					ERR_FAIL_COND_V(!d.has("origin"), Variant());
+				} break;
+				case Variant::TRANSFORM2D: {
+					LOAD_ARGS_CHECK_SIZE(6);
+
 					Transform2D t;
 					Transform2D t;
-					t[0] = to_native(d["x"]);
-					t[1] = to_native(d["y"]);
-					t[2] = to_native(d["origin"]);
+					t[0] = Vector2(args[0], args[1]);
+					t[1] = Vector2(args[2], args[3]);
+					t[2] = Vector2(args[4], args[5]);
+
 					return t;
 					return t;
-				} else if (type == Variant::get_type_name(Variant::VECTOR4)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 4, Variant());
+				} break;
+				case Variant::VECTOR4: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Vector4 v;
 					Vector4 v;
-					v.x = values[0];
-					v.y = values[1];
-					v.z = values[2];
-					v.w = values[3];
+					v.x = args[0];
+					v.y = args[1];
+					v.z = args[2];
+					v.w = args[3];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::VECTOR4I)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 4, Variant());
+				} break;
+				case Variant::VECTOR4I: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Vector4i v;
 					Vector4i v;
-					v.x = values[0];
-					v.y = values[1];
-					v.z = values[2];
-					v.w = values[3];
+					v.x = args[0];
+					v.y = args[1];
+					v.z = args[2];
+					v.w = args[3];
+
 					return v;
 					return v;
-				} else if (type == Variant::get_type_name(Variant::PLANE)) {
-					ERR_FAIL_COND_V(!d.has("normal"), Variant());
-					ERR_FAIL_COND_V(!d.has("d"), Variant());
+				} break;
+				case Variant::PLANE: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Plane p;
 					Plane p;
-					p.normal = to_native(d["normal"]);
-					p.d = d["d"];
+					p.normal = Vector3(args[0], args[1], args[2]);
+					p.d = args[3];
+
 					return p;
 					return p;
-				} else if (type == Variant::get_type_name(Variant::QUATERNION)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 4, Variant());
-					Quaternion v;
-					v.x = values[0];
-					v.y = values[1];
-					v.z = values[2];
-					v.w = values[3];
-					return v;
-				} else if (type == Variant::get_type_name(Variant::AABB)) {
-					ERR_FAIL_COND_V(!d.has("position"), Variant());
-					ERR_FAIL_COND_V(!d.has("size"), Variant());
-					AABB r;
-					r.position = to_native(d["position"]);
-					r.size = to_native(d["size"]);
-					return r;
-				} else if (type == Variant::get_type_name(Variant::BASIS)) {
-					ERR_FAIL_COND_V(!d.has("x"), Variant());
-					ERR_FAIL_COND_V(!d.has("y"), Variant());
-					ERR_FAIL_COND_V(!d.has("z"), Variant());
+				} break;
+				case Variant::QUATERNION: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
+					Quaternion q;
+					q.x = args[0];
+					q.y = args[1];
+					q.z = args[2];
+					q.w = args[3];
+
+					return q;
+				} break;
+				case Variant::AABB: {
+					LOAD_ARGS_CHECK_SIZE(6);
+
+					AABB aabb;
+					aabb.position = Vector3(args[0], args[1], args[2]);
+					aabb.size = Vector3(args[3], args[4], args[5]);
+
+					return aabb;
+				} break;
+				case Variant::BASIS: {
+					LOAD_ARGS_CHECK_SIZE(9);
+
 					Basis b;
 					Basis b;
-					b.set_column(0, to_native(d["x"]));
-					b.set_column(1, to_native(d["y"]));
-					b.set_column(2, to_native(d["z"]));
+					b.set_column(0, Vector3(args[0], args[1], args[2]));
+					b.set_column(1, Vector3(args[3], args[4], args[5]));
+					b.set_column(2, Vector3(args[6], args[7], args[8]));
+
 					return b;
 					return b;
-				} else if (type == Variant::get_type_name(Variant::TRANSFORM3D)) {
-					ERR_FAIL_COND_V(!d.has("basis"), Variant());
-					ERR_FAIL_COND_V(!d.has("origin"), Variant());
+				} break;
+				case Variant::TRANSFORM3D: {
+					LOAD_ARGS_CHECK_SIZE(12);
+
 					Transform3D t;
 					Transform3D t;
-					t.basis = to_native(d["basis"]);
-					t.origin = to_native(d["origin"]);
+					t.basis.set_column(0, Vector3(args[0], args[1], args[2]));
+					t.basis.set_column(1, Vector3(args[3], args[4], args[5]));
+					t.basis.set_column(2, Vector3(args[6], args[7], args[8]));
+					t.origin = Vector3(args[9], args[10], args[11]);
+
 					return t;
 					return t;
-				} else if (type == Variant::get_type_name(Variant::PROJECTION)) {
-					ERR_FAIL_COND_V(!d.has("x"), Variant());
-					ERR_FAIL_COND_V(!d.has("y"), Variant());
-					ERR_FAIL_COND_V(!d.has("z"), Variant());
-					ERR_FAIL_COND_V(!d.has("w"), Variant());
+				} break;
+				case Variant::PROJECTION: {
+					LOAD_ARGS_CHECK_SIZE(16);
+
 					Projection p;
 					Projection p;
-					p[0] = to_native(d["x"]);
-					p[1] = to_native(d["y"]);
-					p[2] = to_native(d["z"]);
-					p[3] = to_native(d["w"]);
+					p[0] = Vector4(args[0], args[1], args[2], args[3]);
+					p[1] = Vector4(args[4], args[5], args[6], args[7]);
+					p[2] = Vector4(args[8], args[9], args[10], args[11]);
+					p[3] = Vector4(args[12], args[13], args[14], args[15]);
+
 					return p;
 					return p;
-				} else if (type == Variant::get_type_name(Variant::COLOR)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() != 4, Variant());
+				} break;
+				case Variant::COLOR: {
+					LOAD_ARGS_CHECK_SIZE(4);
+
 					Color c;
 					Color c;
-					c.r = values[0];
-					c.g = values[1];
-					c.b = values[2];
-					c.a = values[3];
+					c.r = args[0];
+					c.g = args[1];
+					c.b = args[2];
+					c.a = args[3];
+
 					return c;
 					return c;
-				} else if (type == Variant::get_type_name(Variant::NODE_PATH)) {
-					ERR_FAIL_COND_V(!d.has("path"), Variant());
-					NodePath np = d["path"];
-					return np;
-				} else if (type == Variant::get_type_name(Variant::STRING_NAME)) {
-					ERR_FAIL_COND_V(!d.has("name"), Variant());
-					StringName s = d["name"];
-					return s;
-				} else if (type == Variant::get_type_name(Variant::OBJECT)) {
-					ERR_FAIL_COND_V(!d.has("type"), Variant());
-					ERR_FAIL_COND_V(!d.has("properties"), Variant());
-
-					ERR_FAIL_COND_V(!p_allow_classes, Variant());
-
-					String obj_type = d["type"];
-					bool is_script = obj_type == "Script" || ClassDB::is_parent_class(obj_type, "Script");
-					ERR_FAIL_COND_V(!p_allow_scripts && is_script, Variant());
-					Object *obj = ClassDB::instantiate(obj_type);
-					ERR_FAIL_NULL_V(obj, Variant());
-
-					Dictionary p = d["properties"];
-
-					List<Variant> keys;
-					p.get_key_list(&keys);
-
-					for (const Variant &K : keys) {
-						String property = K;
-						Variant value = to_native(p[K], PASS_ARG);
-						obj->set(property, value);
+				} break;
+
+				case Variant::OBJECT: {
+					// Nothing to do at this stage. `Object` should be treated as a class, not as a built-in type.
+				} break;
+
+				case Variant::DICTIONARY: {
+					LOAD_ARGS_CHECK_FACTOR(2);
+
+					ContainerType key_type;
+					if (!_decode_container_type(dict, KEY_TYPE, key_type, p_allow_objects)) {
+						return Variant();
 					}
 					}
 
 
-					Variant v(obj);
+					ContainerType value_type;
+					if (!_decode_container_type(dict, VALUE_TYPE, value_type, p_allow_objects)) {
+						return Variant();
+					}
 
 
-					return v;
-				} else if (type == Variant::get_type_name(Variant::DICTIONARY)) {
-					ERR_FAIL_COND_V(!d.has("pairs"), Variant());
-					Array pairs = d["pairs"];
-					Dictionary r;
-					for (int i = 0; i < pairs.size(); i++) {
-						Dictionary p = pairs[i];
-						ERR_CONTINUE(!p.has("key"));
-						ERR_CONTINUE(!p.has("value"));
-						r[to_native(p["key"], PASS_ARG)] = to_native(p["value"]);
+					Dictionary ret;
+
+					if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
+						ret.set_typed(key_type, value_type);
 					}
 					}
-					return r;
-				} else if (type == Variant::get_type_name(Variant::ARRAY)) {
-					ERR_PRINT(vformat("Unexpected Array with '%s' key. Arrays are supported natively.", GDTYPE));
-				} else if (type == Variant::get_type_name(Variant::PACKED_BYTE_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					PackedByteArray pbarr;
-					pbarr.resize(values.size());
-					for (int i = 0; i < pbarr.size(); i++) {
-						pbarr.write[i] = values[i];
+
+					ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ret, "Variant is too deep. Bailing.");
+
+					for (int i = 0; i < args.size() / 2; i++) {
+						ret[_to_native(args[i * 2 + 0], p_allow_objects, p_depth + 1)] = _to_native(args[i * 2 + 1], p_allow_objects, p_depth + 1);
+					}
+
+					return ret;
+				} break;
+
+				case Variant::ARRAY: {
+					LOAD_ARGS();
+
+					ContainerType elem_type;
+					if (!_decode_container_type(dict, ELEM_TYPE, elem_type, p_allow_objects)) {
+						return Variant();
+					}
+
+					Array ret;
+
+					if (elem_type.builtin_type != Variant::NIL) {
+						ret.set_typed(elem_type);
+					}
+
+					ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ret, "Variant is too deep. Bailing.");
+
+					ret.resize(args.size());
+					for (int i = 0; i < args.size(); i++) {
+						ret[i] = _to_native(args[i], p_allow_objects, p_depth + 1);
+					}
+
+					return ret;
+				} break;
+
+				case Variant::PACKED_BYTE_ARRAY: {
+					LOAD_ARGS();
+
+					PackedByteArray arr;
+					arr.resize(args.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = args[i];
 					}
 					}
-					return pbarr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_INT32_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
+
+					return arr;
+				} break;
+				case Variant::PACKED_INT32_ARRAY: {
+					LOAD_ARGS();
+
 					PackedInt32Array arr;
 					PackedInt32Array arr;
-					arr.resize(values.size());
+					arr.resize(args.size());
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = values[i];
+						arr.write[i] = args[i];
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_INT64_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
+				} break;
+				case Variant::PACKED_INT64_ARRAY: {
+					LOAD_ARGS();
+
 					PackedInt64Array arr;
 					PackedInt64Array arr;
-					arr.resize(values.size());
+					arr.resize(args.size());
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = values[i];
+						arr.write[i] = args[i];
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_FLOAT32_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
+				} break;
+				case Variant::PACKED_FLOAT32_ARRAY: {
+					LOAD_ARGS();
+
 					PackedFloat32Array arr;
 					PackedFloat32Array arr;
-					arr.resize(values.size());
+					arr.resize(args.size());
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = values[i];
+						arr.write[i] = args[i];
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_FLOAT64_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
+				} break;
+				case Variant::PACKED_FLOAT64_ARRAY: {
+					LOAD_ARGS();
+
 					PackedFloat64Array arr;
 					PackedFloat64Array arr;
-					arr.resize(values.size());
+					arr.resize(args.size());
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = values[i];
+						arr.write[i] = args[i];
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_STRING_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
+				} break;
+				case Variant::PACKED_STRING_ARRAY: {
+					LOAD_ARGS();
+
 					PackedStringArray arr;
 					PackedStringArray arr;
-					arr.resize(values.size());
+					arr.resize(args.size());
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = values[i];
+						arr.write[i] = args[i];
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_VECTOR2_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() % 2 != 0, Variant());
+				} break;
+				case Variant::PACKED_VECTOR2_ARRAY: {
+					LOAD_ARGS_CHECK_FACTOR(2);
+
 					PackedVector2Array arr;
 					PackedVector2Array arr;
-					arr.resize(values.size() / 2);
+					arr.resize(args.size() / 2);
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = Vector2(values[i * 2 + 0], values[i * 2 + 1]);
+						arr.write[i] = Vector2(args[i * 2 + 0], args[i * 2 + 1]);
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_VECTOR3_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() % 3 != 0, Variant());
+				} break;
+				case Variant::PACKED_VECTOR3_ARRAY: {
+					LOAD_ARGS_CHECK_FACTOR(3);
+
 					PackedVector3Array arr;
 					PackedVector3Array arr;
-					arr.resize(values.size() / 3);
+					arr.resize(args.size() / 3);
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = Vector3(values[i * 3 + 0], values[i * 3 + 1], values[i * 3 + 2]);
+						arr.write[i] = Vector3(args[i * 3 + 0], args[i * 3 + 1], args[i * 3 + 2]);
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_COLOR_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+				} break;
+				case Variant::PACKED_COLOR_ARRAY: {
+					LOAD_ARGS_CHECK_FACTOR(4);
+
 					PackedColorArray arr;
 					PackedColorArray arr;
-					arr.resize(values.size() / 4);
+					arr.resize(args.size() / 4);
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = Color(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+						arr.write[i] = Color(args[i * 4 + 0], args[i * 4 + 1], args[i * 4 + 2], args[i * 4 + 3]);
 					}
 					}
+
 					return arr;
 					return arr;
-				} else if (type == Variant::get_type_name(Variant::PACKED_VECTOR4_ARRAY)) {
-					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
-					Array values = d[VALUES];
-					ERR_FAIL_COND_V(values.size() % 4 != 0, Variant());
+				} break;
+				case Variant::PACKED_VECTOR4_ARRAY: {
+					LOAD_ARGS_CHECK_FACTOR(4);
+
 					PackedVector4Array arr;
 					PackedVector4Array arr;
-					arr.resize(values.size() / 4);
+					arr.resize(args.size() / 4);
 					for (int i = 0; i < arr.size(); i++) {
 					for (int i = 0; i < arr.size(); i++) {
-						arr.write[i] = Vector4(values[i * 4 + 0], values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);
+						arr.write[i] = Vector4(args[i * 4 + 0], args[i * 4 + 1], args[i * 4 + 2], args[i * 4 + 3]);
 					}
 					}
+
 					return arr;
 					return arr;
+				} break;
+
+				case Variant::VARIANT_MAX: {
+					// Nothing to do.
+				} break;
+			}
+
+#undef LOAD_ARGS
+#undef LOAD_ARGS_CHECK_SIZE
+#undef LOAD_ARGS_CHECK_FACTOR
+
+			if (ClassDB::class_exists(dict[TYPE])) {
+				ERR_FAIL_COND_V(!p_allow_objects, Variant());
+
+				ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, Variant(), "Variant is too deep. Bailing.");
+
+				ERR_FAIL_COND_V(!dict.has(PROPS), Variant());
+				const Array props = dict[PROPS];
+				ERR_FAIL_COND_V(props.size() % 2 != 0, Variant());
+
+				ERR_FAIL_COND_V(!ClassDB::can_instantiate(dict[TYPE]), Variant());
+
+				Object *obj = ClassDB::instantiate(dict[TYPE]);
+				ERR_FAIL_NULL_V(obj, Variant());
+
+				// Avoid premature free `RefCounted`. This must be done before properties are initialized,
+				// since script functions (setters, implicit initializer) may be called. See GH-68666.
+				Variant variant;
+				if (Object::cast_to<RefCounted>(obj)) {
+					const Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
+					variant = ref;
 				} else {
 				} else {
-					return Variant();
+					variant = obj;
 				}
 				}
-			} else {
-				// Regular dictionary with string keys.
-				List<Variant> keys;
-				d.get_key_list(&keys);
-				Dictionary r;
-				for (const Variant &K : keys) {
-					r[K] = to_native(d[K], PASS_ARG);
+
+				for (int i = 0; i < props.size() / 2; i++) {
+					const StringName name = props[i * 2 + 0];
+					const Variant value = _to_native(props[i * 2 + 1], p_allow_objects, p_depth + 1);
+
+					if (name == CoreStringName(script) && value.get_type() != Variant::NIL) {
+						const String path = value;
+						ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"),
+								Variant(),
+								vformat(R"(Invalid script path "%s".)", path));
+
+						const Ref<Script> script = ResourceLoader::load(path, "Script");
+						ERR_FAIL_COND_V_MSG(script.is_null(), Variant(), vformat(R"(Can't load script at path "%s".)", path));
+
+						obj->set_script(script);
+					} else {
+						obj->set(name, value);
+					}
 				}
 				}
-				return r;
+
+				return variant;
 			}
 			}
+
+			ERR_FAIL_V_MSG(Variant(), vformat(R"(Invalid type "%s".)", dict[TYPE]));
 		} break;
 		} break;
+
 		case Variant::ARRAY: {
 		case Variant::ARRAY: {
-			Array arr = p_json;
+			ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, Array(), "Variant is too deep. Bailing.");
+
+			const Array arr = p_json;
+
 			Array ret;
 			Array ret;
 			ret.resize(arr.size());
 			ret.resize(arr.size());
 			for (int i = 0; i < arr.size(); i++) {
 			for (int i = 0; i < arr.size(); i++) {
-				ret[i] = to_native(arr[i], PASS_ARG);
+				ret[i] = _to_native(arr[i], p_allow_objects, p_depth + 1);
 			}
 			}
+
 			return ret;
 			return ret;
 		} break;
 		} break;
+
 		default: {
 		default: {
-			ERR_PRINT(vformat("Unhandled conversion from JSON type '%s' to native Variant type.", Variant::get_type_name(p_json.get_type())));
-			return Variant();
-		}
+			// Nothing to do.
+		} break;
 	}
 	}
 
 
-	return Variant();
+	ERR_FAIL_V_MSG(Variant(), vformat(R"(Variant type "%s" is not JSON-compliant.)", Variant::get_type_name(p_json.get_type())));
 }
 }
 
 
-#undef GDTYPE
-#undef VALUES
-#undef PASS_ARG
+#undef TYPE
+#undef ELEM_TYPE
+#undef KEY_TYPE
+#undef VALUE_TYPE
+#undef ARGS
+#undef PROPS
 
 
 ////////////
 ////////////
 
 

+ 13 - 5
core/io/json.h

@@ -80,6 +80,9 @@ class JSON : public Resource {
 	static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
 	static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
 	static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
 	static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
 
 
+	static Variant _from_native(const Variant &p_variant, bool p_full_objects, int p_depth);
+	static Variant _to_native(const Variant &p_json, bool p_allow_objects, int p_depth);
+
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 
 
@@ -90,13 +93,18 @@ public:
 	static String stringify(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false);
 	static String stringify(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false);
 	static Variant parse_string(const String &p_json_string);
 	static Variant parse_string(const String &p_json_string);
 
 
-	inline Variant get_data() const { return data; }
+	_FORCE_INLINE_ static Variant from_native(const Variant &p_variant, bool p_full_objects = false) {
+		return _from_native(p_variant, p_full_objects, 0);
+	}
+	_FORCE_INLINE_ static Variant to_native(const Variant &p_json, bool p_allow_objects = false) {
+		return _to_native(p_json, p_allow_objects, 0);
+	}
+
 	void set_data(const Variant &p_data);
 	void set_data(const Variant &p_data);
-	inline int get_error_line() const { return err_line; }
-	inline String get_error_message() const { return err_str; }
+	_FORCE_INLINE_ Variant get_data() const { return data; }
 
 
-	static Variant from_native(const Variant &p_variant, bool p_allow_classes = false, bool p_allow_scripts = false);
-	static Variant to_native(const Variant &p_json, bool p_allow_classes = false, bool p_allow_scripts = false);
+	_FORCE_INLINE_ int get_error_line() const { return err_line; }
+	_FORCE_INLINE_ String get_error_message() const { return err_str; }
 };
 };
 
 
 class ResourceFormatLoaderJSON : public ResourceFormatLoader {
 class ResourceFormatLoaderJSON : public ResourceFormatLoader {

+ 14 - 52
core/io/marshalls.cpp

@@ -33,6 +33,7 @@
 #include "core/io/resource_loader.h"
 #include "core/io/resource_loader.h"
 #include "core/object/ref_counted.h"
 #include "core/object/ref_counted.h"
 #include "core/object/script_language.h"
 #include "core/object/script_language.h"
+#include "core/variant/container_type_validate.h"
 
 
 #include <limits.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdio.h>
@@ -84,12 +85,6 @@ enum ContainerTypeKind {
 	CONTAINER_TYPE_KIND_SCRIPT = 0b11,
 	CONTAINER_TYPE_KIND_SCRIPT = 0b11,
 };
 };
 
 
-struct ContainerType {
-	Variant::Type builtin_type = Variant::NIL;
-	StringName class_name;
-	Ref<Script> script;
-};
-
 #define GET_CONTAINER_TYPE_KIND(m_header, m_field) \
 #define GET_CONTAINER_TYPE_KIND(m_header, m_field) \
 	((ContainerTypeKind)(((m_header) & HEADER_DATA_FIELD_##m_field##_MASK) >> HEADER_DATA_FIELD_##m_field##_SHIFT))
 	((ContainerTypeKind)(((m_header) & HEADER_DATA_FIELD_##m_field##_MASK) >> HEADER_DATA_FIELD_##m_field##_SHIFT))
 
 
@@ -844,7 +839,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 			Dictionary dict;
 			Dictionary dict;
 			if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
 			if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
-				dict.set_typed(key_type.builtin_type, key_type.class_name, key_type.script, value_type.builtin_type, value_type.class_name, value_type.script);
+				dict.set_typed(key_type, value_type);
 			}
 			}
 
 
 			for (int i = 0; i < count; i++) {
 			for (int i = 0; i < count; i++) {
@@ -901,7 +896,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 			Array array;
 			Array array;
 			if (type.builtin_type != Variant::NIL) {
 			if (type.builtin_type != Variant::NIL) {
-				array.set_typed(type.builtin_type, type.class_name, type.script);
+				array.set_typed(type);
 			}
 			}
 
 
 			for (int i = 0; i < count; i++) {
 			for (int i = 0; i < count; i++) {
@@ -1402,31 +1397,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 			}
 			}
 		} break;
 		} break;
 		case Variant::DICTIONARY: {
 		case Variant::DICTIONARY: {
-			Dictionary dict = p_variant;
-
-			ContainerType key_type;
-			key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
-			key_type.class_name = dict.get_typed_key_class_name();
-			key_type.script = dict.get_typed_key_script();
-
-			_encode_container_type_header(key_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT, p_full_objects);
-
-			ContainerType value_type;
-			value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
-			value_type.class_name = dict.get_typed_value_class_name();
-			value_type.script = dict.get_typed_value_script();
-
-			_encode_container_type_header(value_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT, p_full_objects);
+			const Dictionary dict = p_variant;
+			_encode_container_type_header(dict.get_key_type(), header, HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT, p_full_objects);
+			_encode_container_type_header(dict.get_value_type(), header, HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT, p_full_objects);
 		} break;
 		} break;
 		case Variant::ARRAY: {
 		case Variant::ARRAY: {
-			Array array = p_variant;
-
-			ContainerType type;
-			type.builtin_type = (Variant::Type)array.get_typed_builtin();
-			type.class_name = array.get_typed_class_name();
-			type.script = array.get_typed_script();
-
-			_encode_container_type_header(type, header, HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT, p_full_objects);
+			const Array array = p_variant;
+			_encode_container_type_header(array.get_element_type(), header, HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT, p_full_objects);
 		} break;
 		} break;
 #ifdef REAL_T_IS_DOUBLE
 #ifdef REAL_T_IS_DOUBLE
 		case Variant::VECTOR2:
 		case Variant::VECTOR2:
@@ -1850,27 +1827,17 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 			r_len += 8;
 			r_len += 8;
 		} break;
 		} break;
 		case Variant::DICTIONARY: {
 		case Variant::DICTIONARY: {
-			Dictionary dict = p_variant;
+			const Dictionary dict = p_variant;
 
 
 			{
 			{
-				ContainerType key_type;
-				key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
-				key_type.class_name = dict.get_typed_key_class_name();
-				key_type.script = dict.get_typed_key_script();
-
-				Error err = _encode_container_type(key_type, buf, r_len, p_full_objects);
+				Error err = _encode_container_type(dict.get_key_type(), buf, r_len, p_full_objects);
 				if (err) {
 				if (err) {
 					return err;
 					return err;
 				}
 				}
 			}
 			}
 
 
 			{
 			{
-				ContainerType value_type;
-				value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
-				value_type.class_name = dict.get_typed_value_class_name();
-				value_type.script = dict.get_typed_value_script();
-
-				Error err = _encode_container_type(value_type, buf, r_len, p_full_objects);
+				Error err = _encode_container_type(dict.get_value_type(), buf, r_len, p_full_objects);
 				if (err) {
 				if (err) {
 					return err;
 					return err;
 				}
 				}
@@ -1894,7 +1861,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 				if (buf) {
 				if (buf) {
 					buf += len;
 					buf += len;
 				}
 				}
-				Variant *value = dict.getptr(key);
+				const Variant *value = dict.getptr(key);
 				ERR_FAIL_NULL_V(value, ERR_BUG);
 				ERR_FAIL_NULL_V(value, ERR_BUG);
 				err = encode_variant(*value, buf, len, p_full_objects, p_depth + 1);
 				err = encode_variant(*value, buf, len, p_full_objects, p_depth + 1);
 				ERR_FAIL_COND_V(err, err);
 				ERR_FAIL_COND_V(err, err);
@@ -1907,15 +1874,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 
 		} break;
 		} break;
 		case Variant::ARRAY: {
 		case Variant::ARRAY: {
-			Array array = p_variant;
+			const Array array = p_variant;
 
 
 			{
 			{
-				ContainerType type;
-				type.builtin_type = (Variant::Type)array.get_typed_builtin();
-				type.class_name = array.get_typed_class_name();
-				type.script = array.get_typed_script();
-
-				Error err = _encode_container_type(type, buf, r_len, p_full_objects);
+				Error err = _encode_container_type(array.get_element_type(), buf, r_len, p_full_objects);
 				if (err) {
 				if (err) {
 					return err;
 					return err;
 				}
 				}

+ 4 - 3
core/math/expression.cpp

@@ -473,10 +473,11 @@ Error Expression::_get_token(Token &r_token) {
 					} else if (id == "self") {
 					} else if (id == "self") {
 						r_token.type = TK_SELF;
 						r_token.type = TK_SELF;
 					} else {
 					} else {
-						for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-							if (id == Variant::get_type_name(Variant::Type(i))) {
+						{
+							const Variant::Type type = Variant::get_type_by_name(id);
+							if (type < Variant::VARIANT_MAX) {
 								r_token.type = TK_BASIC_TYPE;
 								r_token.type = TK_BASIC_TYPE;
-								r_token.value = i;
+								r_token.value = type;
 								return OK;
 								return OK;
 							}
 							}
 						}
 						}

+ 13 - 2
core/variant/array.cpp

@@ -41,8 +41,7 @@
 #include "core/variant/dictionary.h"
 #include "core/variant/dictionary.h"
 #include "core/variant/variant.h"
 #include "core/variant/variant.h"
 
 
-class ArrayPrivate {
-public:
+struct ArrayPrivate {
 	SafeRefCount refcount;
 	SafeRefCount refcount;
 	Vector<Variant> array;
 	Vector<Variant> array;
 	Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values.
 	Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values.
@@ -843,6 +842,10 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
 	assign(p_from);
 	assign(p_from);
 }
 }
 
 
+void Array::set_typed(const ContainerType &p_element_type) {
+	set_typed(p_element_type.builtin_type, p_element_type.class_name, p_element_type.script);
+}
+
 void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
 void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
 	ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
 	ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
 	ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
 	ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
@@ -870,6 +873,14 @@ bool Array::is_same_instance(const Array &p_other) const {
 	return _p == p_other._p;
 	return _p == p_other._p;
 }
 }
 
 
+ContainerType Array::get_element_type() const {
+	ContainerType type;
+	type.builtin_type = _p->typed.type;
+	type.class_name = _p->typed.class_name;
+	type.script = _p->typed.script;
+	return type;
+}
+
 uint32_t Array::get_typed_builtin() const {
 uint32_t Array::get_typed_builtin() const {
 	return _p->typed.type;
 	return _p->typed.type;
 }
 }

+ 9 - 3
core/variant/array.h

@@ -35,11 +35,13 @@
 
 
 #include <climits>
 #include <climits>
 
 
-class Variant;
-class ArrayPrivate;
+class Callable;
 class Object;
 class Object;
 class StringName;
 class StringName;
-class Callable;
+class Variant;
+
+struct ArrayPrivate;
+struct ContainerType;
 
 
 class Array {
 class Array {
 	mutable ArrayPrivate *_p;
 	mutable ArrayPrivate *_p;
@@ -185,10 +187,14 @@ public:
 
 
 	const void *id() const;
 	const void *id() const;
 
 
+	void set_typed(const ContainerType &p_element_type);
 	void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
 	void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
+
 	bool is_typed() const;
 	bool is_typed() const;
 	bool is_same_typed(const Array &p_other) const;
 	bool is_same_typed(const Array &p_other) const;
 	bool is_same_instance(const Array &p_other) const;
 	bool is_same_instance(const Array &p_other) const;
+
+	ContainerType get_element_type() const;
 	uint32_t get_typed_builtin() const;
 	uint32_t get_typed_builtin() const;
 	StringName get_typed_class_name() const;
 	StringName get_typed_class_name() const;
 	Variant get_typed_script() const;
 	Variant get_typed_script() const;

+ 6 - 0
core/variant/container_type_validate.h

@@ -34,6 +34,12 @@
 #include "core/object/script_language.h"
 #include "core/object/script_language.h"
 #include "core/variant/variant.h"
 #include "core/variant/variant.h"
 
 
+struct ContainerType {
+	Variant::Type builtin_type = Variant::NIL;
+	StringName class_name;
+	Ref<Script> script;
+};
+
 struct ContainerTypeValidate {
 struct ContainerTypeValidate {
 	Variant::Type type = Variant::NIL;
 	Variant::Type type = Variant::NIL;
 	StringName class_name;
 	StringName class_name;

+ 20 - 0
core/variant/dictionary.cpp

@@ -595,6 +595,10 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
 	return n;
 	return n;
 }
 }
 
 
+void Dictionary::set_typed(const ContainerType &p_key_type, const ContainerType &p_value_type) {
+	set_typed(p_key_type.builtin_type, p_key_type.class_name, p_key_type.script, p_value_type.builtin_type, p_value_type.class_name, p_key_type.script);
+}
+
 void Dictionary::set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script) {
 void Dictionary::set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script) {
 	ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
 	ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
 	ERR_FAIL_COND_MSG(_p->variant_map.size() > 0, "Type can only be set when dictionary is empty.");
 	ERR_FAIL_COND_MSG(_p->variant_map.size() > 0, "Type can only be set when dictionary is empty.");
@@ -641,6 +645,22 @@ bool Dictionary::is_same_typed_value(const Dictionary &p_other) const {
 	return _p->typed_value == p_other._p->typed_value;
 	return _p->typed_value == p_other._p->typed_value;
 }
 }
 
 
+ContainerType Dictionary::get_key_type() const {
+	ContainerType type;
+	type.builtin_type = _p->typed_key.type;
+	type.class_name = _p->typed_key.class_name;
+	type.script = _p->typed_key.script;
+	return type;
+}
+
+ContainerType Dictionary::get_value_type() const {
+	ContainerType type;
+	type.builtin_type = _p->typed_value.type;
+	type.class_name = _p->typed_value.class_name;
+	type.script = _p->typed_value.script;
+	return type;
+}
+
 uint32_t Dictionary::get_typed_key_builtin() const {
 uint32_t Dictionary::get_typed_key_builtin() const {
 	return _p->typed_key.type;
 	return _p->typed_key.type;
 }
 }

+ 6 - 0
core/variant/dictionary.h

@@ -38,6 +38,7 @@
 
 
 class Variant;
 class Variant;
 
 
+struct ContainerType;
 struct DictionaryPrivate;
 struct DictionaryPrivate;
 
 
 class Dictionary {
 class Dictionary {
@@ -92,13 +93,18 @@ public:
 	Dictionary duplicate(bool p_deep = false) const;
 	Dictionary duplicate(bool p_deep = false) const;
 	Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
 	Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
 
 
+	void set_typed(const ContainerType &p_key_type, const ContainerType &p_value_type);
 	void set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script);
 	void set_typed(uint32_t p_key_type, const StringName &p_key_class_name, const Variant &p_key_script, uint32_t p_value_type, const StringName &p_value_class_name, const Variant &p_value_script);
+
 	bool is_typed() const;
 	bool is_typed() const;
 	bool is_typed_key() const;
 	bool is_typed_key() const;
 	bool is_typed_value() const;
 	bool is_typed_value() const;
 	bool is_same_typed(const Dictionary &p_other) const;
 	bool is_same_typed(const Dictionary &p_other) const;
 	bool is_same_typed_key(const Dictionary &p_other) const;
 	bool is_same_typed_key(const Dictionary &p_other) const;
 	bool is_same_typed_value(const Dictionary &p_other) const;
 	bool is_same_typed_value(const Dictionary &p_other) const;
+
+	ContainerType get_key_type() const;
+	ContainerType get_value_type() const;
 	uint32_t get_typed_key_builtin() const;
 	uint32_t get_typed_key_builtin() const;
 	uint32_t get_typed_value_builtin() const;
 	uint32_t get_typed_value_builtin() const;
 	StringName get_typed_key_class_name() const;
 	StringName get_typed_key_class_name() const;

+ 12 - 0
core/variant/variant.cpp

@@ -176,6 +176,18 @@ String Variant::get_type_name(Variant::Type p_type) {
 	return "";
 	return "";
 }
 }
 
 
+Variant::Type Variant::get_type_by_name(const String &p_type_name) {
+	static HashMap<String, Type> type_names;
+	if (unlikely(type_names.is_empty())) {
+		for (int i = 0; i < VARIANT_MAX; i++) {
+			type_names[get_type_name((Type)i)] = (Type)i;
+		}
+	}
+
+	const Type *ptr = type_names.getptr(p_type_name);
+	return (ptr == nullptr) ? VARIANT_MAX : *ptr;
+}
+
 bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
 	if (p_type_from == p_type_to) {
 	if (p_type_from == p_type_to) {
 		return true;
 		return true;

+ 1 - 0
core/variant/variant.h

@@ -354,6 +354,7 @@ public:
 		return type;
 		return type;
 	}
 	}
 	static String get_type_name(Variant::Type p_type);
 	static String get_type_name(Variant::Type p_type);
+	static Variant::Type get_type_by_name(const String &p_type_name);
 	static bool can_convert(Type p_type_from, Type p_type_to);
 	static bool can_convert(Type p_type_from, Type p_type_to);
 	static bool can_convert_strict(Type p_type_from, Type p_type_to);
 	static bool can_convert_strict(Type p_type_from, Type p_type_to);
 	static bool is_type_shared(Variant::Type p_type);
 	static bool is_type_shared(Variant::Type p_type);

+ 16 - 8
doc/classes/JSON.xml

@@ -40,11 +40,15 @@
 		<method name="from_native" qualifiers="static">
 		<method name="from_native" qualifiers="static">
 			<return type="Variant" />
 			<return type="Variant" />
 			<param index="0" name="variant" type="Variant" />
 			<param index="0" name="variant" type="Variant" />
-			<param index="1" name="allow_classes" type="bool" default="false" />
-			<param index="2" name="allow_scripts" type="bool" default="false" />
+			<param index="1" name="full_objects" type="bool" default="false" />
 			<description>
 			<description>
-				Converts a native engine type to a JSON-compliant dictionary.
-				By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified.
+				Converts a native engine type to a JSON-compliant value.
+				By default, objects are ignored for security reasons, unless [param full_objects] is [code]true[/code].
+				You can convert a native value to a JSON string like this:
+				[codeblock]
+				func encode_data(value, full_objects = false):
+				    return JSON.stringify(JSON.from_native(value, full_objects))
+				[/codeblock]
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="get_error_line" qualifiers="const">
 		<method name="get_error_line" qualifiers="const">
@@ -136,11 +140,15 @@
 		<method name="to_native" qualifiers="static">
 		<method name="to_native" qualifiers="static">
 			<return type="Variant" />
 			<return type="Variant" />
 			<param index="0" name="json" type="Variant" />
 			<param index="0" name="json" type="Variant" />
-			<param index="1" name="allow_classes" type="bool" default="false" />
-			<param index="2" name="allow_scripts" type="bool" default="false" />
+			<param index="1" name="allow_objects" type="bool" default="false" />
 			<description>
 			<description>
-				Converts a JSON-compliant dictionary that was created with [method from_native] back to native engine types.
-				By default, classes and scripts are ignored for security reasons, unless [param allow_classes] or [param allow_scripts] are specified.
+				Converts a JSON-compliant value that was created with [method from_native] back to native engine types.
+				By default, objects are ignored for security reasons, unless [param allow_objects] is [code]true[/code].
+				You can convert a JSON string back to a native value like this:
+				[codeblock]
+				func decode_data(string, allow_objects = false):
+				    return JSON.to_native(JSON.parse_string(string), allow_objects)
+				[/codeblock]
 			</description>
 			</description>
 		</method>
 		</method>
 	</methods>
 	</methods>

+ 5 - 7
editor/editor_autoload_settings.cpp

@@ -112,14 +112,12 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
 		return false;
 		return false;
 	}
 	}
 
 
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		if (Variant::get_type_name(Variant::Type(i)) == p_name) {
-			if (r_error) {
-				*r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing built-in type name.");
-			}
-
-			return false;
+	if (Variant::get_type_by_name(p_name) < Variant::VARIANT_MAX) {
+		if (r_error) {
+			*r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing built-in type name.");
 		}
 		}
+
+		return false;
 	}
 	}
 
 
 	for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
 	for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {

+ 4 - 7
modules/gdscript/gdscript_editor.cpp

@@ -3861,13 +3861,10 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
 		return OK;
 		return OK;
 	}
 	}
 
 
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		Variant::Type t = Variant::Type(i);
-		if (Variant::get_type_name(t) == p_symbol) {
-			r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;
-			r_result.class_name = Variant::get_type_name(t);
-			return OK;
-		}
+	if (Variant::get_type_by_name(p_symbol) < Variant::VARIANT_MAX) {
+		r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;
+		r_result.class_name = p_symbol;
+		return OK;
 	}
 	}
 
 
 	if ("Variant" == p_symbol) {
 	if ("Variant" == p_symbol) {

+ 184 - 117
tests/core/io/test_json_native.h

@@ -33,128 +33,195 @@
 
 
 #include "core/io/json.h"
 #include "core/io/json.h"
 
 
+#include "core/variant/typed_array.h"
+#include "core/variant/typed_dictionary.h"
+#include "tests/test_macros.h"
+
 namespace TestJSONNative {
 namespace TestJSONNative {
 
 
-bool compare_variants(Variant variant_1, Variant variant_2, int depth = 0) {
-	if (depth > 100) {
-		return false;
-	}
-	if (variant_1.get_type() == Variant::RID && variant_2.get_type() == Variant::RID) {
-		return true;
-	}
-	if (variant_1.get_type() == Variant::CALLABLE || variant_2.get_type() == Variant::CALLABLE) {
-		return true;
-	}
-
-	List<PropertyInfo> variant_1_properties;
-	variant_1.get_property_list(&variant_1_properties);
-	List<PropertyInfo> variant_2_properties;
-	variant_2.get_property_list(&variant_2_properties);
-
-	if (variant_1_properties.size() != variant_2_properties.size()) {
-		return false;
-	}
-
-	for (List<PropertyInfo>::Element *E = variant_1_properties.front(); E; E = E->next()) {
-		String name = E->get().name;
-		Variant variant_1_value = variant_1.get(name);
-		Variant variant_2_value = variant_2.get(name);
-
-		if (!compare_variants(variant_1_value, variant_2_value, depth + 1)) {
-			return false;
-		}
-	}
-
-	return true;
+String encode(const Variant &p_variant, bool p_full_objects = false) {
+	return JSON::stringify(JSON::from_native(p_variant, p_full_objects), "", false);
+}
+
+Variant decode(const String &p_string, bool p_allow_objects = false) {
+	return JSON::to_native(JSON::parse_string(p_string), p_allow_objects);
 }
 }
 
 
-TEST_CASE("[JSON][Native][SceneTree] Conversion between native and JSON formats") {
-	for (int variant_i = 0; variant_i < Variant::VARIANT_MAX; variant_i++) {
-		Variant::Type type = static_cast<Variant::Type>(variant_i);
-		Variant native_data;
-		Callable::CallError error;
-
-		if (type == Variant::Type::INT || type == Variant::Type::FLOAT) {
-			Variant value = int64_t(INT64_MAX);
-			const Variant *args[] = { &value };
-			Variant::construct(type, native_data, args, 1, error);
-		} else if (type == Variant::Type::OBJECT) {
-			Ref<JSON> json = memnew(JSON);
-			native_data = json;
-		} else if (type == Variant::Type::DICTIONARY) {
-			Dictionary dictionary;
-			dictionary["key"] = "value";
-			native_data = dictionary;
-		} else if (type == Variant::Type::ARRAY) {
-			Array array;
-			array.push_back("element1");
-			array.push_back("element2");
-			native_data = array;
-		} else if (type == Variant::Type::PACKED_BYTE_ARRAY) {
-			PackedByteArray packed_array;
-			packed_array.push_back(1);
-			packed_array.push_back(2);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_INT32_ARRAY) {
-			PackedInt32Array packed_array;
-			packed_array.push_back(INT32_MIN);
-			packed_array.push_back(INT32_MAX);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_INT64_ARRAY) {
-			PackedInt64Array packed_array;
-			packed_array.push_back(INT64_MIN);
-			packed_array.push_back(INT64_MAX);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_FLOAT32_ARRAY) {
-			PackedFloat32Array packed_array;
-			packed_array.push_back(FLT_MIN);
-			packed_array.push_back(FLT_MAX);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_FLOAT64_ARRAY) {
-			PackedFloat64Array packed_array;
-			packed_array.push_back(DBL_MIN);
-			packed_array.push_back(DBL_MAX);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_STRING_ARRAY) {
-			PackedStringArray packed_array;
-			packed_array.push_back("string1");
-			packed_array.push_back("string2");
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_VECTOR2_ARRAY) {
-			PackedVector2Array packed_array;
-			Vector2 vector(1.0, 2.0);
-			packed_array.push_back(vector);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_VECTOR3_ARRAY) {
-			PackedVector3Array packed_array;
-			Vector3 vector(1.0, 2.0, 3.0);
-			packed_array.push_back(vector);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_COLOR_ARRAY) {
-			PackedColorArray packed_array;
-			Color color(1.0, 1.0, 1.0);
-			packed_array.push_back(color);
-			native_data = packed_array;
-		} else if (type == Variant::Type::PACKED_VECTOR4_ARRAY) {
-			PackedVector4Array packed_array;
-			Vector4 vector(1.0, 2.0, 3.0, 4.0);
-			packed_array.push_back(vector);
-			native_data = packed_array;
-		} else {
-			Variant::construct(type, native_data, nullptr, 0, error);
-		}
-		Variant json_converted_from_native = JSON::from_native(native_data, true, true);
-		Variant variant_native_converted = JSON::to_native(json_converted_from_native, true, true);
-		CHECK_MESSAGE(compare_variants(native_data, variant_native_converted),
-				vformat("Conversion from native to JSON type %s and back successful. \nNative: %s \nNative Converted: %s \nError: %s\nConversion from native to JSON type %s successful: %s",
-						Variant::get_type_name(type),
-						native_data,
-						variant_native_converted,
-						itos(error.error),
-						Variant::get_type_name(type),
-						json_converted_from_native));
-	}
+void test(const Variant &p_variant, const String &p_string, bool p_with_objects = false) {
+	CHECK(encode(p_variant, p_with_objects) == p_string);
+	CHECK(decode(p_string, p_with_objects).get_construct_string() == p_variant.get_construct_string());
+}
+
+TEST_CASE("[JSON][Native] Conversion between native and JSON formats") {
+	// `Nil` and `bool` (represented as JSON keyword literals).
+	test(Variant(), "null");
+	test(false, "false");
+	test(true, "true");
+
+	// Numbers and strings (represented as JSON strings).
+	test(1, R"("i:1")");
+	test(1.0, R"("f:1.0")");
+	test(String("abc"), R"("s:abc")");
+	test(StringName("abc"), R"("sn:abc")");
+	test(NodePath("abc"), R"("np:abc")");
+
+	// Non-serializable types (always empty after deserialization).
+	test(RID(), R"({"type":"RID"})");
+	test(Callable(), R"({"type":"Callable"})");
+	test(Signal(), R"({"type":"Signal"})");
+
+	// Math types.
+
+	test(Vector2(1, 2), R"({"type":"Vector2","args":[1.0,2.0]})");
+	test(Vector2i(1, 2), R"({"type":"Vector2i","args":[1,2]})");
+	test(Rect2(1, 2, 3, 4), R"({"type":"Rect2","args":[1.0,2.0,3.0,4.0]})");
+	test(Rect2i(1, 2, 3, 4), R"({"type":"Rect2i","args":[1,2,3,4]})");
+	test(Vector3(1, 2, 3), R"({"type":"Vector3","args":[1.0,2.0,3.0]})");
+	test(Vector3i(1, 2, 3), R"({"type":"Vector3i","args":[1,2,3]})");
+	test(Transform2D(1, 2, 3, 4, 5, 6), R"({"type":"Transform2D","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
+	test(Vector4(1, 2, 3, 4), R"({"type":"Vector4","args":[1.0,2.0,3.0,4.0]})");
+	test(Vector4i(1, 2, 3, 4), R"({"type":"Vector4i","args":[1,2,3,4]})");
+	test(Plane(1, 2, 3, 4), R"({"type":"Plane","args":[1.0,2.0,3.0,4.0]})");
+	test(Quaternion(1, 2, 3, 4), R"({"type":"Quaternion","args":[1.0,2.0,3.0,4.0]})");
+	test(AABB(Vector3(1, 2, 3), Vector3(4, 5, 6)), R"({"type":"AABB","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
+
+	const Basis b(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9));
+	test(b, R"({"type":"Basis","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]})");
+
+	const Transform3D tr3d(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(10, 11, 12));
+	test(tr3d, R"({"type":"Transform3D","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
+
+	const Projection p(Vector4(1, 2, 3, 4), Vector4(5, 6, 7, 8), Vector4(9, 10, 11, 12), Vector4(13, 14, 15, 16));
+	test(p, R"({"type":"Projection","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0]})");
+
+	test(Color(1, 2, 3, 4), R"({"type":"Color","args":[1.0,2.0,3.0,4.0]})");
+
+	// `Object`.
+
+	Ref<Resource> res;
+	res.instantiate();
+
+	// The properties are stored in an array because the order in which they are assigned may be important during initialization.
+	const String res_repr = R"({"type":"Resource","props":["resource_local_to_scene",false,"resource_name","s:","script",null]})";
+
+	test(res, res_repr, true);
+	ERR_PRINT_OFF;
+	CHECK(encode(res) == "null");
+	CHECK(decode(res_repr).get_type() == Variant::NIL);
+	ERR_PRINT_ON;
+
+	// `Dictionary`.
+
+	Dictionary dict;
+	dict[false] = true;
+	dict[0] = 1;
+	dict[0.0] = 1.0;
+
+	// Godot dictionaries preserve insertion order, so an array is used for keys/values.
+	test(dict, R"({"type":"Dictionary","args":[false,true,"i:0","i:1","f:0.0","f:1.0"]})");
+
+	TypedDictionary<int64_t, int64_t> int_int_dict;
+	int_int_dict[1] = 2;
+	int_int_dict[3] = 4;
+
+	test(int_int_dict, R"({"type":"Dictionary","key_type":"int","value_type":"int","args":["i:1","i:2","i:3","i:4"]})");
+
+	TypedDictionary<int64_t, Variant> int_var_dict;
+	int_var_dict[1] = "2";
+	int_var_dict[3] = "4";
+
+	test(int_var_dict, R"({"type":"Dictionary","key_type":"int","args":["i:1","s:2","i:3","s:4"]})");
+
+	TypedDictionary<Variant, int64_t> var_int_dict;
+	var_int_dict["1"] = 2;
+	var_int_dict["3"] = 4;
+
+	test(var_int_dict, R"({"type":"Dictionary","value_type":"int","args":["s:1","i:2","s:3","i:4"]})");
+
+	Dictionary dict2;
+	dict2["x"] = res;
+
+	const String dict2_repr = vformat(R"({"type":"Dictionary","args":["s:x",%s]})", res_repr);
+
+	test(dict2, dict2_repr, true);
+	ERR_PRINT_OFF;
+	CHECK(encode(dict2) == R"({"type":"Dictionary","args":["s:x",null]})");
+	CHECK(decode(dict2_repr).get_construct_string() == "{\n\"x\": null\n}");
+	ERR_PRINT_ON;
+
+	TypedDictionary<String, Resource> res_dict;
+	res_dict["x"] = res;
+
+	const String res_dict_repr = vformat(R"({"type":"Dictionary","key_type":"String","value_type":"Resource","args":["s:x",%s]})", res_repr);
+
+	test(res_dict, res_dict_repr, true);
+	ERR_PRINT_OFF;
+	CHECK(encode(res_dict) == "null");
+	CHECK(decode(res_dict_repr).get_type() == Variant::NIL);
+	ERR_PRINT_ON;
+
+	// `Array`.
+
+	Array arr;
+	arr.push_back(true);
+	arr.push_back(1);
+	arr.push_back("abc");
+
+	test(arr, R"([true,"i:1","s:abc"])");
+
+	TypedArray<int64_t> int_arr;
+	int_arr.push_back(1);
+	int_arr.push_back(2);
+	int_arr.push_back(3);
+
+	test(int_arr, R"({"type":"Array","elem_type":"int","args":["i:1","i:2","i:3"]})");
+
+	Array arr2;
+	arr2.push_back(1);
+	arr2.push_back(res);
+	arr2.push_back(9);
+
+	const String arr2_repr = vformat(R"(["i:1",%s,"i:9"])", res_repr);
+
+	test(arr2, arr2_repr, true);
+	ERR_PRINT_OFF;
+	CHECK(encode(arr2) == R"(["i:1",null,"i:9"])");
+	CHECK(decode(arr2_repr).get_construct_string() == "[1, null, 9]");
+	ERR_PRINT_ON;
+
+	TypedArray<Resource> res_arr;
+	res_arr.push_back(res);
+
+	const String res_arr_repr = vformat(R"({"type":"Array","elem_type":"Resource","args":[%s]})", res_repr);
+
+	test(res_arr, res_arr_repr, true);
+	ERR_PRINT_OFF;
+	CHECK(encode(res_arr) == "null");
+	CHECK(decode(res_arr_repr).get_type() == Variant::NIL);
+	ERR_PRINT_ON;
+
+	// Packed arrays.
+
+	test(PackedByteArray({ 1, 2, 3 }), R"({"type":"PackedByteArray","args":[1,2,3]})");
+	test(PackedInt32Array({ 1, 2, 3 }), R"({"type":"PackedInt32Array","args":[1,2,3]})");
+	test(PackedInt64Array({ 1, 2, 3 }), R"({"type":"PackedInt64Array","args":[1,2,3]})");
+	test(PackedFloat32Array({ 1, 2, 3 }), R"({"type":"PackedFloat32Array","args":[1.0,2.0,3.0]})");
+	test(PackedFloat64Array({ 1, 2, 3 }), R"({"type":"PackedFloat64Array","args":[1.0,2.0,3.0]})");
+	test(PackedStringArray({ "a", "b", "c" }), R"({"type":"PackedStringArray","args":["a","b","c"]})");
+
+	const PackedVector2Array pv2arr({ Vector2(1, 2), Vector2(3, 4), Vector2(5, 6) });
+	test(pv2arr, R"({"type":"PackedVector2Array","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
+
+	const PackedVector3Array pv3arr({ Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9) });
+	test(pv3arr, R"({"type":"PackedVector3Array","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]})");
+
+	const PackedColorArray pcolarr({ Color(1, 2, 3, 4), Color(5, 6, 7, 8), Color(9, 10, 11, 12) });
+	test(pcolarr, R"({"type":"PackedColorArray","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
+
+	const PackedVector4Array pv4arr({ Vector4(1, 2, 3, 4), Vector4(5, 6, 7, 8), Vector4(9, 10, 11, 12) });
+	test(pv4arr, R"({"type":"PackedVector4Array","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
 }
 }
+
 } // namespace TestJSONNative
 } // namespace TestJSONNative
 
 
 #endif // TEST_JSON_NATIVE_H
 #endif // TEST_JSON_NATIVE_H