Treat JSON as resource files.
This makes the files ended in ".json" be treated as Godot resources. This solves two problems: * Avoid extensions to implement their own handling, which results in conflicts (all must use this one). * Allow code to still work opening it as a file (since it will not be imported).
This commit is contained in:
parent
b8977ca333
commit
2b428daf2b
4 changed files with 127 additions and 11 deletions
|
@ -497,6 +497,10 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JSON::set_data(const Variant &p_data) {
|
||||||
|
data = p_data;
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
const char32_t *str = p_json.ptr();
|
const char32_t *str = p_json.ptr();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
@ -557,6 +561,88 @@ void JSON::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse);
|
ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse);
|
||||||
|
|
||||||
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("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);
|
||||||
|
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
Ref<Resource> ResourceFormatLoaderJSON::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = ERR_FILE_CANT_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FileAccess::exists(p_path)) {
|
||||||
|
*r_error = ERR_FILE_NOT_FOUND;
|
||||||
|
return Ref<Resource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<JSON> json;
|
||||||
|
json.instantiate();
|
||||||
|
|
||||||
|
Error err = json->parse(FileAccess::get_file_as_string(p_path));
|
||||||
|
if (err != OK) {
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = err;
|
||||||
|
}
|
||||||
|
ERR_PRINT("Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message());
|
||||||
|
return Ref<Resource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r_error) {
|
||||||
|
*r_error = OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceFormatLoaderJSON::get_recognized_extensions(List<String> *p_extensions) const {
|
||||||
|
p_extensions->push_back("json");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceFormatLoaderJSON::handles_type(const String &p_type) const {
|
||||||
|
return (p_type == "JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
String ResourceFormatLoaderJSON::get_resource_type(const String &p_path) const {
|
||||||
|
String el = p_path.get_extension().to_lower();
|
||||||
|
if (el == "json") {
|
||||||
|
return "JSON";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Error ResourceFormatSaverJSON::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
|
||||||
|
Ref<JSON> json = p_resource;
|
||||||
|
ERR_FAIL_COND_V(json.is_null(), ERR_INVALID_PARAMETER);
|
||||||
|
|
||||||
|
String source = JSON::stringify(json->get_data(), "\t", false, true);
|
||||||
|
|
||||||
|
Error err;
|
||||||
|
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V_MSG(err, err, "Cannot save json '" + p_path + "'.");
|
||||||
|
|
||||||
|
file->store_string(source);
|
||||||
|
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
|
||||||
|
return ERR_CANT_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceFormatSaverJSON::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
|
||||||
|
Ref<JSON> json = p_resource;
|
||||||
|
if (json.is_valid()) {
|
||||||
|
p_extensions->push_back("json");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResourceFormatSaverJSON::recognize(const Ref<Resource> &p_resource) const {
|
||||||
|
return p_resource->get_class_name() == "JSON"; //only json, not inherited
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
#ifndef JSON_H
|
#ifndef JSON_H
|
||||||
#define JSON_H
|
#define JSON_H
|
||||||
|
|
||||||
#include "core/object/ref_counted.h"
|
#include "core/io/resource.h"
|
||||||
|
#include "core/io/resource_loader.h"
|
||||||
|
#include "core/io/resource_saver.h"
|
||||||
#include "core/variant/variant.h"
|
#include "core/variant/variant.h"
|
||||||
|
|
||||||
class JSON : public RefCounted {
|
class JSON : public RefCounted {
|
||||||
|
@ -86,8 +88,24 @@ public:
|
||||||
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; }
|
inline Variant get_data() const { return data; }
|
||||||
|
void set_data(const Variant &p_data);
|
||||||
inline int get_error_line() const { return err_line; }
|
inline int get_error_line() const { return err_line; }
|
||||||
inline String get_error_message() const { return err_str; }
|
inline String get_error_message() const { return err_str; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ResourceFormatLoaderJSON : public ResourceFormatLoader {
|
||||||
|
public:
|
||||||
|
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
|
||||||
|
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||||
|
virtual bool handles_type(const String &p_type) const;
|
||||||
|
virtual String get_resource_type(const String &p_path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourceFormatSaverJSON : public ResourceFormatSaver {
|
||||||
|
public:
|
||||||
|
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
|
||||||
|
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
|
||||||
|
virtual bool recognize(const Ref<Resource> &p_resource) const;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // JSON_H
|
#endif // JSON_H
|
||||||
|
|
|
@ -87,6 +87,8 @@ static Ref<TranslationLoaderPO> resource_format_po;
|
||||||
static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto;
|
static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto;
|
||||||
static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto;
|
static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto;
|
||||||
static Ref<NativeExtensionResourceLoader> resource_loader_native_extension;
|
static Ref<NativeExtensionResourceLoader> resource_loader_native_extension;
|
||||||
|
static Ref<ResourceFormatSaverJSON> resource_saver_json;
|
||||||
|
static Ref<ResourceFormatLoaderJSON> resource_loader_json;
|
||||||
|
|
||||||
static core_bind::ResourceLoader *_resource_loader = nullptr;
|
static core_bind::ResourceLoader *_resource_loader = nullptr;
|
||||||
static core_bind::ResourceSaver *_resource_saver = nullptr;
|
static core_bind::ResourceSaver *_resource_saver = nullptr;
|
||||||
|
@ -211,6 +213,12 @@ void register_core_types() {
|
||||||
resource_format_loader_crypto.instantiate();
|
resource_format_loader_crypto.instantiate();
|
||||||
ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);
|
ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);
|
||||||
|
|
||||||
|
resource_loader_json.instantiate();
|
||||||
|
ResourceLoader::add_resource_format_loader(resource_loader_json);
|
||||||
|
|
||||||
|
resource_saver_json.instantiate();
|
||||||
|
ResourceSaver::add_resource_format_saver(resource_saver_json);
|
||||||
|
|
||||||
GDREGISTER_CLASS(MainLoop);
|
GDREGISTER_CLASS(MainLoop);
|
||||||
GDREGISTER_CLASS(Translation);
|
GDREGISTER_CLASS(Translation);
|
||||||
GDREGISTER_CLASS(OptimizedTranslation);
|
GDREGISTER_CLASS(OptimizedTranslation);
|
||||||
|
@ -387,6 +395,12 @@ void unregister_core_types() {
|
||||||
ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto);
|
ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto);
|
||||||
resource_format_loader_crypto.unref();
|
resource_format_loader_crypto.unref();
|
||||||
|
|
||||||
|
ResourceSaver::remove_resource_format_saver(resource_saver_json);
|
||||||
|
resource_saver_json.unref();
|
||||||
|
|
||||||
|
ResourceLoader::remove_resource_format_loader(resource_loader_json);
|
||||||
|
resource_loader_json.unref();
|
||||||
|
|
||||||
if (ip) {
|
if (ip) {
|
||||||
memdelete(ip);
|
memdelete(ip);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<description>
|
<description>
|
||||||
The [JSON] enables all data types to be converted to and from a JSON string. This useful for serializing data to save to a file or send over the network.
|
The [JSON] enables all data types to be converted to and from a JSON string. This useful for serializing data to save to a file or send over the network.
|
||||||
[method stringify] is used to convert any data type into a JSON string.
|
[method stringify] is used to convert any data type into a JSON string.
|
||||||
[method parse] is used to convert any existing JSON data into a [Variant] that can be used within Godot. If successfully parsed, use [method get_data] to retrieve the [Variant], and use [code]typeof[/code] to check if the Variant's type is what you expect. JSON Objects are converted into a [Dictionary], but JSON data can be used to store [Array]s, numbers, [String]s and even just a boolean.
|
[method parse] is used to convert any existing JSON data into a [Variant] that can be used within Godot. If successfully parsed, use [member data] to retrieve the [Variant], and use [code]typeof[/code] to check if the Variant's type is what you expect. JSON Objects are converted into a [Dictionary], but JSON data can be used to store [Array]s, numbers, [String]s and even just a boolean.
|
||||||
[b]Example[/b]
|
[b]Example[/b]
|
||||||
[codeblock]
|
[codeblock]
|
||||||
var data_to_send = ["a", "b", "c"]
|
var data_to_send = ["a", "b", "c"]
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
# Retrieve data
|
# Retrieve data
|
||||||
var error = json.parse(json_string)
|
var error = json.parse(json_string)
|
||||||
if error == OK:
|
if error == OK:
|
||||||
var data_received = json.get_data()
|
var data_received = json.data
|
||||||
if typeof(data_received) == TYPE_ARRAY:
|
if typeof(data_received) == TYPE_ARRAY:
|
||||||
print(data_received) # Prints array
|
print(data_received) # Prints array
|
||||||
else:
|
else:
|
||||||
|
@ -32,13 +32,6 @@
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
<methods>
|
<methods>
|
||||||
<method name="get_data" qualifiers="const">
|
|
||||||
<return type="Variant" />
|
|
||||||
<description>
|
|
||||||
Returns the [Variant] containing the data of a successful [method parse].
|
|
||||||
[b]Note:[/b] It will return [code]Null[/code] if the last call to parse was unsuccessful or [method parse] has not yet been called.
|
|
||||||
</description>
|
|
||||||
</method>
|
|
||||||
<method name="get_error_line" qualifiers="const">
|
<method name="get_error_line" qualifiers="const">
|
||||||
<return type="int" />
|
<return type="int" />
|
||||||
<description>
|
<description>
|
||||||
|
@ -56,7 +49,7 @@
|
||||||
<param index="0" name="json_string" type="String" />
|
<param index="0" name="json_string" type="String" />
|
||||||
<description>
|
<description>
|
||||||
Attempts to parse the [param json_string] provided.
|
Attempts to parse the [param json_string] provided.
|
||||||
Returns an [enum Error]. If the parse was successful, it returns [code]OK[/code] and the result can be retrieved using [method get_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 [code]OK[/code] 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.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
@ -118,4 +111,9 @@
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
</methods>
|
</methods>
|
||||||
|
<members>
|
||||||
|
<member name="data" type="Variant" setter="set_data" getter="get_data" default="null">
|
||||||
|
Contains the parsed JSON data in [Variant] form.
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
</class>
|
</class>
|
||||||
|
|
Loading…
Reference in a new issue