跳转至

Variant 深拷贝与 JSON 转换

通用 Variant 基础件分为三个明确职责:GFVariantData 负责深拷贝、字典 / metadata 合并、options 读取、基础类型收窄、默认值合并和 Resource 可选复制;GFVariantJsonCodec 负责 JSON 友好的 Godot 值类型转换;GFVariantReferenceCodec 负责显式 Resource / Node 引用标记。

它们都不依赖 GFArchitecture,适合存档、配置、校验报告、网络消息、命中上下文等需要复制集合但保留标量语义,或把 Godot 值转成纯数据的地方。

深拷贝与默认值

var payload := {
    "stats": {
        "hp": 10,
    },
}
var copy := GFVariantData.duplicate_variant(payload) as Dictionary

var settings := {
    "audio": {
        "volume": 0.8,
    },
}
GFVariantData.deep_merge_defaults(settings, {
    "audio": {
        "mute": false,
    },
    "language": "zh",
})

GFVariantData.duplicate_variant() 默认只深拷贝 DictionaryArray,其他值保持原样返回;如果值中包含 ObjectResource,仍是引用语义。需要复制资源值时,可显式传入 duplicate_variant(value, true, true)

Metadata 与 Options

项目自定义 metadata 应保持普通 Dictionary,框架只复制、合并和透传,不解释业务键。需要合并时优先使用 merge_metadata(),避免不同模块手写深拷贝和嵌套合并规则:

var metadata := GFVariantData.duplicate_metadata(base_metadata)
GFVariantData.merge_metadata(metadata, {
    "source": "importer",
    "tags": ["preview"],
})

公共 API 的 options 字典应使用稳定字段名,并通过 get_option_bool()get_option_int()get_option_float()get_option_dictionary() 等读取。读取器支持 StringStringName 键互查,集合返回副本,避免调用方和框架共享内部状态。merge_dictionary() / merge_metadata() 判断已有字段时也遵循同一套等价键规则,因此不会因为来源字典使用 StringName、目标字典使用 String 而生成重复字段。

Variant 收窄

当数据来自 Dictionary.get()、反射调用、JSON 解码、网络消息或编辑器配置时,先使用 GFVariantData 做显式收窄,再进入业务逻辑:

var retry_count := GFVariantData.to_int(options.get("retry_count", 0), 0)
var enabled := GFVariantData.to_bool(options.get("enabled", true), true)
var route_id := GFVariantData.to_string_name(record.get("route_id", &""))

to_bool()to_int()to_float()to_text()to_string_name()to_vector2()to_vector3() 都要求调用方显式给出 fallback 语义;非法文本不会被静默解释为 0falseVector2 / Vector3 收窄支持同维或相邻维度向量、x/y/z 字典和数值数组。常见标量集合可用 to_string_array()to_string_name_array()to_int_array() 逐项归一并返回副本,options 字段则对应使用 get_option_string_array()get_option_string_name_array()get_option_int_array()

集合有两组入口:as_dictionary() / as_array() 返回原引用,适合继续修改运行时状态;to_dictionary() / to_array() 返回副本,适合公开快照、metadata、options 和持久化数据。对象、Resource、节点、Callable 等领域类型仍应由具体模块本地收窄,不放进通用 Variant 工具。

JSON 兼容转换

var saved_position := GFVariantJsonCodec.vector2_to_array(Vector2(12.0, 4.0))
var position := GFVariantJsonCodec.array_to_vector2(saved_position)

var json_payload := GFVariantJsonCodec.variant_to_json_compatible({
    "position": Vector3(1.0, 2.0, 3.0),
    "tags": PackedStringArray(["state.ready"]),
})
var restored := GFVariantJsonCodec.json_compatible_to_variant(
    JSON.parse_string(JSON.stringify(json_payload))
) as Dictionary

var pretty_json := GFVariantJsonCodec.format_json_text("{\"b\":2,\"a\":1}", "  ", true)
var compact_json := GFVariantJsonCodec.compact_json_text(pretty_json)

GFVariantJsonCodec.variant_to_json_compatible() 会为 Vector2/3/4、整数向量、ColorRect2Transform2D/3DBasisQuaternionAABBPlaneNodePathStringName 和常见 PackedArray 写入专用 __gf_variant__ 类型标记,再由 json_compatible_to_variant() 恢复。

parse_json_text()format_json_text()compact_json_text() 面向已经是 JSON 文本的输入:它们先通过 Godot JSON 解析器确认文本有效,再返回解析值、格式化文本或去除非必要空白后的文本。解析失败时返回调用方提供的 fallback,不会把无效输入静默改写成空集合。

显式引用标记

GFVariantReferenceCodec 用于少数确实需要保存对象引用的位置。Resource 引用会保存 resource_path、可用时的 ResourceUID 文本和类型提示;恢复时先尝试 UID,再回退到路径。Node 引用只保存相对调用方提供 root 的 NodePath,解码时也必须传入同一个语义 root,不会从场景树全局搜索。

var encoded_resource := GFVariantReferenceCodec.encode_reference(texture)
var decoded_resource := GFVariantReferenceCodec.decode_reference(encoded_resource)

var encoded_node := GFVariantReferenceCodec.encode_reference(target_node, {
    GFVariantReferenceCodec.OPTION_ROOT_NODE: scope_root,
})
var decoded_node := GFVariantReferenceCodec.decode_reference(encoded_node, {
    GFVariantReferenceCodec.OPTION_ROOT_NODE: scope_root,
})

引用 codec 不处理对象图、脚本自动实例化、无路径内嵌 Resource、跨 Scope 节点扫描或业务身份映射。需要保存复杂对象时,项目应先转换成稳定 ID、Resource 路径、显式 NodePath 或纯数据字典。

使用边界

普通整数在 JSON 安全范围内仍保持数字;超出 JSON 安全范围的 64 位整数会自动写成 Int64 类型标记,避免 Godot JSON 往返后丢失精度。只有 __gf_variant__ 标记是字典唯一字段时才会被解码为 Godot 类型,因此普通业务字典里的 typevalue_gf_type 等字段会按普通数据保留。

默认普通 Dictionary 仍使用字符串键;如果确实需要保留非字符串键,可传 { "encode_dictionary_keys": true }。JSON codec 遇到不支持的对象默认写成 null;需要持久化对象时,应在项目层先转换成资源路径、ID 或纯数据字典。