Browse Source

Ability to convert native engine types to JSON and back.

Implements support for all engine types in JSON encoding/decoding

Co-Authored-By: Juan <[email protected]>
Co-Authored-By: Rémi Verschelde <[email protected]>
K. S. Ernest (iFire) Lee 1 year ago
parent
commit
71bdbcdfb1
5 changed files with 931 additions and 1 deletions
  1. 747 1
      core/io/json.cpp
  2. 3 0
      core/io/json.h
  3. 20 0
      doc/classes/JSON.xml
  4. 160 0
      tests/core/io/test_json_native.h
  5. 1 0
      tests/test_main.cpp

+ 747 - 1
core/io/json.cpp

@@ -588,10 +588,756 @@ void JSON::_bind_methods() {
 	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_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));
+
 	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
+
+Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) {
+	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::BOOL: {
+			return p_variant;
+		} break;
+		case Variant::INT: {
+			return p_variant;
+		} break;
+		case Variant::FLOAT: {
+			return p_variant;
+		} break;
+		case Variant::STRING: {
+			return p_variant;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		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;
+		} break;
+		case Variant::OBJECT: {
+			Object *obj = p_variant.get_validated_object();
+
+			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);
+					}
+				}
+				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;
+			}
+		} 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: {
+			Dictionary d = p_variant;
+			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;
+				}
+			}
+
+			if (all_strings) {
+				Dictionary ret_dict;
+				for (const Variant &K : keys) {
+					ret_dict[K] = from_native(d[K], PASS_ARG);
+				}
+				return ret_dict;
+			} 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;
+			}
+		} break;
+		case Variant::ARRAY: {
+			Array arr = p_variant;
+			Array ret;
+			for (int i = 0; i < arr.size(); i++) {
+				ret.push_back(from_native(arr[i], PASS_ARG));
+			}
+			return ret;
+		} break;
+		case Variant::PACKED_BYTE_ARRAY: {
+			Dictionary d;
+			PackedByteArray arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_INT32_ARRAY: {
+			Dictionary d;
+			PackedInt32Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+
+		} break;
+		case Variant::PACKED_INT64_ARRAY: {
+			Dictionary d;
+			PackedInt64Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_FLOAT32_ARRAY: {
+			Dictionary d;
+			PackedFloat32Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_FLOAT64_ARRAY: {
+			Dictionary d;
+			PackedFloat64Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_STRING_ARRAY: {
+			Dictionary d;
+			PackedStringArray arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				values.push_back(arr[i]);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_VECTOR2_ARRAY: {
+			Dictionary d;
+			PackedVector2Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				Vector2 v = arr[i];
+				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;
+		} break;
+		case Variant::PACKED_VECTOR3_ARRAY: {
+			Dictionary d;
+			PackedVector3Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); i++) {
+				Vector3 v = arr[i];
+				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;
+		} break;
+		case Variant::PACKED_COLOR_ARRAY: {
+			Dictionary d;
+			PackedColorArray arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); 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);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		case Variant::PACKED_VECTOR4_ARRAY: {
+			Dictionary d;
+			PackedVector4Array arr = p_variant;
+			Array values;
+			for (int i = 0; i < arr.size(); 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);
+			}
+			d[VALUES] = values;
+			d[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+			return d;
+		} break;
+		default: {
+			ERR_PRINT(vformat("Unhandled conversion from native Variant type '%s' to JSON.", Variant::get_type_name(p_variant.get_type())));
+		} break;
+	}
+
+	Dictionary nil;
+	nil[GDTYPE] = Variant::get_type_name(p_variant.get_type());
+	return nil;
+}
+
+Variant JSON::to_native(const Variant &p_json, bool p_allow_classes, bool p_allow_scripts) {
+	switch (p_json.get_type()) {
+		case Variant::BOOL: {
+			return p_json;
+		} break;
+		case Variant::INT: {
+			return p_json;
+		} break;
+		case Variant::FLOAT: {
+			return p_json;
+		} break;
+		case Variant::STRING: {
+			return p_json;
+		} break;
+		case Variant::STRING_NAME: {
+			return p_json;
+		} break;
+		case Variant::CALLABLE: {
+			return p_json;
+		} break;
+		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());
+					Vector2 v;
+					v.x = values[0];
+					v.y = values[1];
+					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());
+					Vector2i v;
+					v.x = values[0];
+					v.y = values[1];
+					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());
+					Rect2 r;
+					r.position = to_native(d["position"]);
+					r.size = to_native(d["size"]);
+					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());
+					Rect2i r;
+					r.position = to_native(d["position"]);
+					r.size = to_native(d["size"]);
+					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());
+					Vector3 v;
+					v.x = values[0];
+					v.y = values[1];
+					v.z = values[2];
+					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());
+					Vector3i v;
+					v.x = values[0];
+					v.y = values[1];
+					v.z = values[2];
+					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());
+					Transform2D t;
+					t[0] = to_native(d["x"]);
+					t[1] = to_native(d["y"]);
+					t[2] = to_native(d["origin"]);
+					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());
+					Vector4 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::VECTOR4I)) {
+					ERR_FAIL_COND_V(!d.has(VALUES), Variant());
+					Array values = d[VALUES];
+					ERR_FAIL_COND_V(values.size() != 4, Variant());
+					Vector4i 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::PLANE)) {
+					ERR_FAIL_COND_V(!d.has("normal"), Variant());
+					ERR_FAIL_COND_V(!d.has("d"), Variant());
+					Plane p;
+					p.normal = to_native(d["normal"]);
+					p.d = d["d"];
+					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());
+					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"]));
+					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());
+					Transform3D t;
+					t.basis = to_native(d["basis"]);
+					t.origin = to_native(d["origin"]);
+					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());
+					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"]);
+					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());
+					Color c;
+					c.r = values[0];
+					c.g = values[1];
+					c.b = values[2];
+					c.a = values[3];
+					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);
+					}
+
+					Variant v(obj);
+
+					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"]);
+					}
+					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];
+					}
+					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];
+					PackedInt32Array arr;
+					arr.resize(values.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = values[i];
+					}
+					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];
+					PackedInt64Array arr;
+					arr.resize(values.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = values[i];
+					}
+					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];
+					PackedFloat32Array arr;
+					arr.resize(values.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = values[i];
+					}
+					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];
+					PackedFloat64Array arr;
+					arr.resize(values.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = values[i];
+					}
+					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];
+					PackedStringArray arr;
+					arr.resize(values.size());
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = values[i];
+					}
+					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());
+					PackedVector2Array arr;
+					arr.resize(values.size() / 2);
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = Vector2(values[i * 2 + 0], values[i * 2 + 1]);
+					}
+					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());
+					PackedVector3Array arr;
+					arr.resize(values.size() / 3);
+					for (int i = 0; i < arr.size(); i++) {
+						arr.write[i] = Vector3(values[i * 3 + 0], values[i * 3 + 1], values[i * 3 + 2]);
+					}
+					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());
+					PackedColorArray arr;
+					arr.resize(values.size() / 4);
+					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]);
+					}
+					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());
+					PackedVector4Array arr;
+					arr.resize(values.size() / 4);
+					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]);
+					}
+					return arr;
+				} else {
+					return Variant();
+				}
+			} 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);
+				}
+				return r;
+			}
+		} break;
+		case Variant::ARRAY: {
+			Array arr = p_json;
+			Array ret;
+			ret.resize(arr.size());
+			for (int i = 0; i < arr.size(); i++) {
+				ret[i] = to_native(arr[i], PASS_ARG);
+			}
+			return ret;
+		} break;
+		default: {
+			ERR_PRINT(vformat("Unhandled conversion from JSON type '%s' to native Variant type.", Variant::get_type_name(p_json.get_type())));
+			return Variant();
+		}
+	}
+
+	return Variant();
+}
+
+#undef GDTYPE
+#undef VALUES
+#undef PASS_ARG
 
 ////////////
 

+ 3 - 0
core/io/json.h

@@ -94,6 +94,9 @@ public:
 	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; }
+
+	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);
 };
 
 class ResourceFormatLoaderJSON : public ResourceFormatLoader {

+ 20 - 0
doc/classes/JSON.xml

@@ -38,6 +38,16 @@
 	<tutorials>
 	</tutorials>
 	<methods>
+		<method name="from_native" qualifiers="static">
+			<return 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" />
+			<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.
+			</description>
+		</method>
 		<method name="get_error_line" qualifiers="const">
 			<return type="int" />
 			<description>
@@ -124,6 +134,16 @@
 				[/codeblock]
 			</description>
 		</method>
+		<method name="to_native" qualifiers="static">
+			<return 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" />
+			<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.
+			</description>
+		</method>
 	</methods>
 	<members>
 		<member name="data" type="Variant" setter="set_data" getter="get_data" default="null">

+ 160 - 0
tests/core/io/test_json_native.h

@@ -0,0 +1,160 @@
+/**************************************************************************/
+/*  test_json_native.h                                                    */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#ifndef TEST_JSON_NATIVE_H
+#define TEST_JSON_NATIVE_H
+
+#include "core/io/json.h"
+
+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;
+}
+
+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));
+	}
+}
+} // namespace TestJSONNative
+
+#endif // TEST_JSON_NATIVE_H

+ 1 - 0
tests/test_main.cpp

@@ -48,6 +48,7 @@
 #include "tests/core/io/test_image.h"
 #include "tests/core/io/test_ip.h"
 #include "tests/core/io/test_json.h"
+#include "tests/core/io/test_json_native.h"
 #include "tests/core/io/test_marshalls.h"
 #include "tests/core/io/test_pck_packer.h"
 #include "tests/core/io/test_resource.h"