You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JsonConvert.PopulateObject填充自定义可序列化类时值被重置问题

Unity JsonConvert.PopulateObject 自定义类字段填充异常问题解析

问题背景

  • 现有系统通过JsonConvert.PopulateObject将JSON填充回ScriptableObject,顶层列表、数组等类型工作正常
  • 自定义可序列化类UpgradeLevel(非ScriptableObject子类)出现异常:未标记JsonProperty或标记JsonIgnore的字段(如GameObject visual、int cost、Vector3 test)填充后被重置为默认值(visual为null、cost为0、test为(0,0,0))
  • 给cost和test标记JsonProperty后可正常取值,但visual(GameObject引用)始终变为null
  • 需求:避免为每个自定义类编写转换器,实现批量兼容

异常原因分析

1. 自定义契约解析器的过滤逻辑

你使用的NoPropertiesContractResolver大概率会过滤未标记JsonProperty的字段。Newtonsoft.Json默认会自动识别类的可序列化字段,但自定义契约解析器如果重写了字段筛选逻辑,会导致未标记特性的字段无法被反序列化框架识别。再加上设置了ObjectCreationHandling = ObjectCreationHandling.Replace,填充时未被识别的字段会被强制重置为默认值。

2. Unity引用类型的序列化限制

Newtonsoft.Json本身无法直接序列化/反序列化Unity的Object子类(如GameObject),这类对象的引用是Unity编辑器/运行时的内部实例,无法通过纯文本JSON存储和直接恢复。即使标记了JsonProperty,JSON中只能存储对象的名称或路径,反序列化时无法自动映射回场景/资源中的实际实例。

解决方案

一、修复自定义类字段被重置问题

1. 调整契约解析器逻辑

修改NoPropertiesContractResolver,让它自动包含所有可序列化字段(无需手动标记JsonProperty):

public class NoPropertiesContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        // 仅保留字段,排除属性
        var properties = base.CreateProperties(type, memberSerialization)
            .Where(p => p.MemberType == MemberTypes.Field)
            .ToList();

        // 补充所有未被筛选到的可序列化字段(排除标记JsonIgnore的)
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(f => f.IsSerializable && !Attribute.IsDefined(f, typeof(JsonIgnoreAttribute))))
        {
            if (!properties.Any(p => p.PropertyName == field.Name))
            {
                properties.Add(CreateProperty(field, memberSerialization));
            }
        }
        return properties;
    }
}

2. 修改对象创建策略

将ObjectCreationHandling从Replace改为Auto,这样反序列化时会优先复用现有对象的字段,而非直接替换整个对象导致未处理字段被重置:

ObjectCreationHandling = ObjectCreationHandling.Auto

二、统一处理Unity引用类型(GameObject)

编写一个通用的Unity对象转换器,无需为每个类单独编写:

public class UnityObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(UnityEngine.Object).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        string assetPath = reader.Value.ToString();
        // 编辑器下通过路径加载资源,运行时需改为从AssetBundle或Resources加载
        return AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var obj = value as UnityEngine.Object;
        if (obj == null)
        {
            writer.WriteNull();
            return;
        }
        // 存储资源路径,仅编辑器有效,运行时可替换为InstanceID或其他唯一标识
        string assetPath = AssetDatabase.GetAssetPath(obj);
        writer.WriteValue(assetPath);
    }
}

将该转换器加入到CreateConverters()的返回列表中,即可统一处理所有Unity Object类型的序列化/反序列化。

内容的提问来源于stack exchange,提问作者Steven Goulet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 05:05:20