Fixes to JSON as resource

* It was not a resource, hence it was not working to load it as such.
* Changed so, when opened in editor, a parse error will not fail load and the text will be kept.
* This should allow proper editing from within the code editor, including syntax checking and saving files as-is in text.

Partially addresses #66820.

The code editor still needs to be changed for this to work.
This commit is contained in:
Juan Linietsky 2023-01-14 12:57:28 +01:00
parent a51ca2beaf
commit 1aaff63b8f
3 changed files with 42 additions and 14 deletions

View File

@ -30,6 +30,7 @@
#include "json.h" #include "json.h"
#include "core/config/engine.h"
#include "core/string/print_string.h" #include "core/string/print_string.h"
const char *JSON::tk_name[TK_MAX] = { const char *JSON::tk_name[TK_MAX] = {
@ -506,6 +507,7 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
void JSON::set_data(const Variant &p_data) { void JSON::set_data(const Variant &p_data) {
data = p_data; data = p_data;
text.clear();
} }
Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) { Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) {
@ -539,14 +541,21 @@ Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_st
return err; return err;
} }
Error JSON::parse(const String &p_json_string) { Error JSON::parse(const String &p_json_string, bool p_keep_text) {
Error err = _parse_string(p_json_string, data, err_str, err_line); Error err = _parse_string(p_json_string, data, err_str, err_line);
if (err == Error::OK) { if (err == Error::OK) {
err_line = 0; err_line = 0;
} }
if (p_keep_text) {
text = p_json_string;
}
return err; return err;
} }
String JSON::get_parsed_text() const {
return text;
}
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; Ref<JSON> jason;
jason.instantiate(); jason.instantiate();
@ -565,10 +574,11 @@ Variant JSON::parse_string(const String &p_json_string) {
void JSON::_bind_methods() { void JSON::_bind_methods() {
ClassDB::bind_static_method("JSON", D_METHOD("stringify", "data", "indent", "sort_keys", "full_precision"), &JSON::stringify, DEFVAL(""), DEFVAL(true), DEFVAL(false)); ClassDB::bind_static_method("JSON", D_METHOD("stringify", "data", "indent", "sort_keys", "full_precision"), &JSON::stringify, DEFVAL(""), DEFVAL(true), DEFVAL(false));
ClassDB::bind_static_method("JSON", D_METHOD("parse_string", "json_string"), &JSON::parse_string); ClassDB::bind_static_method("JSON", D_METHOD("parse_string", "json_string"), &JSON::parse_string);
ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse); ClassDB::bind_method(D_METHOD("parse", "json_text", "keep_text"), &JSON::parse, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_data"), &JSON::get_data); ClassDB::bind_method(D_METHOD("get_data"), &JSON::get_data);
ClassDB::bind_method(D_METHOD("set_data", "data"), &JSON::set_data); ClassDB::bind_method(D_METHOD("set_data", "data"), &JSON::set_data);
ClassDB::bind_method(D_METHOD("get_parsed_text"), &JSON::get_parsed_text);
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);
@ -592,14 +602,21 @@ Ref<Resource> ResourceFormatLoaderJSON::load(const String &p_path, const String
Ref<JSON> json; Ref<JSON> json;
json.instantiate(); json.instantiate();
Error err = json->parse(FileAccess::get_file_as_string(p_path)); Error err = json->parse(FileAccess::get_file_as_string(p_path), Engine::get_singleton()->is_editor_hint());
if (err != OK) { if (err != OK) {
String err_text = "Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message();
if (Engine::get_singleton()->is_editor_hint()) {
// If running on editor, still allow opening the JSON so the code editor can edit it.
WARN_PRINT(err_text);
} else {
if (r_error) { if (r_error) {
*r_error = err; *r_error = err;
} }
ERR_PRINT("Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message()); ERR_PRINT(err_text);
return Ref<Resource>(); return Ref<Resource>();
} }
}
if (r_error) { if (r_error) {
*r_error = OK; *r_error = OK;
@ -628,7 +645,7 @@ Error ResourceFormatSaverJSON::save(const Ref<Resource> &p_resource, const Strin
Ref<JSON> json = p_resource; Ref<JSON> json = p_resource;
ERR_FAIL_COND_V(json.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(json.is_null(), ERR_INVALID_PARAMETER);
String source = JSON::stringify(json->get_data(), "\t", false, true); String source = json->get_parsed_text().is_empty() ? JSON::stringify(json->get_data(), "\t", false, true) : json->get_parsed_text();
Error err; Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);

View File

@ -36,8 +36,8 @@
#include "core/io/resource_saver.h" #include "core/io/resource_saver.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
class JSON : public RefCounted { class JSON : public Resource {
GDCLASS(JSON, RefCounted); GDCLASS(JSON, Resource);
enum TokenType { enum TokenType {
TK_CURLY_BRACKET_OPEN, TK_CURLY_BRACKET_OPEN,
@ -65,6 +65,7 @@ class JSON : public RefCounted {
Variant value; Variant value;
}; };
String text;
Variant data; Variant data;
String err_str; String err_str;
int err_line = 0; int err_line = 0;
@ -83,7 +84,9 @@ protected:
static void _bind_methods(); static void _bind_methods();
public: public:
Error parse(const String &p_json_string); Error parse(const String &p_json_string, bool p_keep_text = false);
String get_parsed_text() const;
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);

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<class name="JSON" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <class name="JSON" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description> <brief_description>
Helper class for creating and parsing JSON data. Helper class for creating and parsing JSON data.
</brief_description> </brief_description>
@ -49,13 +49,21 @@
Returns an empty string if the last call to [method parse] was successful, or the error message if it failed. Returns an empty string if the last call to [method parse] was successful, or the error message if it failed.
</description> </description>
</method> </method>
<method name="get_parsed_text" qualifiers="const">
<return type="String" />
<description>
Return the text parsed by [method parse] as long as the function is instructed to keep it.
</description>
</method>
<method name="parse"> <method name="parse">
<return type="int" enum="Error" /> <return type="int" enum="Error" />
<param index="0" name="json_string" type="String" /> <param index="0" name="json_text" type="String" />
<param index="1" name="keep_text" type="bool" default="false" />
<description> <description>
Attempts to parse the [param json_string] provided. Attempts to parse the [param json_text] provided.
Returns an [enum Error]. If the parse was successful, it returns [constant OK] and the result can be retrieved using [member data]. If unsuccessful, use [method get_error_line] and [method get_error_message] for identifying the source of the failure. Returns an [enum Error]. If the parse was successful, it returns [constant OK] and the result can be retrieved using [member data]. If unsuccessful, use [method get_error_line] and [method get_error_message] for identifying the source of the failure.
Non-static variant of [method parse_string], if you want custom error handling. Non-static variant of [method parse_string], if you want custom error handling.
The optional [param keep_text] argument instructs the parser to keep a copy of the original text. This text can be obtained later by using the [method get_parsed_text] function and is used when saving the resource (instead of generating new text from [member data]).
</description> </description>
</method> </method>
<method name="parse_string" qualifiers="static"> <method name="parse_string" qualifiers="static">