signalis-eb/godot/addons/godot_object_serializer/dictionary_serializer.gd
2025-06-06 19:32:51 -04:00

101 lines
3.7 KiB
GDScript

## Serializer to be used with JSON serialization ([method JSON.stringify] and [method JSON.parse_string]).
## This serializes objects and built-in Godot types.
class_name DictionarySerializer
# Types that can natively be represented in JSON
const _JSON_SERIALIZABLE_TYPES = [
TYPE_NIL, TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_STRING_NAME
]
## Controls if [PackedByteArray] should be serialized as base64 (instead of array of bytes as uint8).
## It's highly recommended to leave this enabled as it will result to smaller serialized payloads and should be faster.
## This can be changed, but must be configured before any serialization or deserialization.
static var bytes_as_base64 := true
## The type of the object for [PackedByteArray] when [member bytes_as_base64] is enabled.
## This should be set to something unlikely to clash with built-in type names or [member ObjectSerializer.object_type_prefix].
## This can be changed, but must be configured before any serialization or deserialization.
static var bytes_to_base64_type := "PackedByteArray_Base64"
## Serialize [param data] into value which can be passed to [method JSON.stringify].
static func serialize_var(value: Variant) -> Variant:
match typeof(value):
TYPE_OBJECT:
var name: StringName = value.get_script().get_global_name()
var object_entry := ObjectSerializer._get_entry(name, value.get_script())
if !object_entry:
assert(
false,
(
"Could not find type (%s) in registry\n%s"
% [name if name else "no name", value.get_script().source_code]
)
)
return object_entry.serialize(value, serialize_var)
TYPE_ARRAY:
return value.map(serialize_var)
TYPE_DICTIONARY:
var result := {}
for i: Variant in value:
result[i] = serialize_var(value[i])
return result
TYPE_PACKED_BYTE_ARRAY:
if bytes_as_base64:
return {
ObjectSerializer.type_field: bytes_to_base64_type,
ObjectSerializer.args_field:
Marshalls.raw_to_base64(value) if !value.is_empty() else ""
}
if _JSON_SERIALIZABLE_TYPES.has(typeof(value)):
return value
return {
ObjectSerializer.type_field: type_string(typeof(value)),
ObjectSerializer.args_field: JSON.from_native(value)["args"]
}
## Serialize [param data] into JSON string with [method DictionaryObjectSerializer.serialize_var] and [method JSON.stringify]. Supports same arguments as [method JSON.stringify]
static func serialize_json(
value: Variant, indent := "", sort_keys := true, full_precision := false
) -> String:
return JSON.stringify(serialize_var(value), indent, sort_keys, full_precision)
## Deserialize [data] from [JSON.parse_string] into value.
static func deserialize_var(value: Variant) -> Variant:
match typeof(value):
TYPE_DICTIONARY:
if value.has(ObjectSerializer.type_field):
var type: String = value.get(ObjectSerializer.type_field)
if bytes_as_base64 and type == bytes_to_base64_type:
return Marshalls.base64_to_raw(value[ObjectSerializer.args_field])
if type.begins_with(ObjectSerializer.object_type_prefix):
var entry := ObjectSerializer._get_entry(type)
if !entry:
assert(false, "Could not find type (%s) in registry" % type)
return entry.deserialize(value, deserialize_var, true)
return JSON.to_native({"type": type, "args": value[ObjectSerializer.args_field]})
var result := {}
for i: Variant in value:
result[i] = deserialize_var(value[i])
return result
TYPE_ARRAY:
return value.map(deserialize_var)
return value
## Deserialize JSON string [param data] to value with [method JSON.parse_string] and [method DictionaryObjectSerializer.deserialize_var].
static func deserialize_json(value: String) -> Variant:
return deserialize_var(JSON.parse_string(value))