Unity中C#自定义标记类的序列化解析继承方案问题
解决方案:Unity多态标记存读系统的实现问题
你的核心问题出在三个关键点:方法重载而非重写、Unity JsonUtility不支持多态反序列化,以及缺少类型识别的分发机制。以下是具体的落地解决方案:
1. 修正方法重写逻辑
你当前TextNodeParser中的CreateMarkerObject是重载(参数为TextMarkerData),而非对基类MarkerData参数方法的重写。C#方法调用是编译时绑定,基类循环传入MarkerData类型时,只会触发基类方法,不会自动匹配派生类的重载。
修改派生类方法签名,确保是合法重写:
public class TextNodeParser : NodeParser { // 参数与基类保持一致,确保是重写而非重载 public override void CreateMarkerObject(MarkerData data) { // 先判断类型,转为子类后处理专属逻辑 if (data is TextMarkerData textData) { // 文本标记专属创建逻辑:比如生成带TextMeshPro的对象 GameObject textMarker = new GameObject("TextMarker"); textMarker.transform.position = textData.pos; var textComp = textMarker.AddComponent<TextMeshPro>(); textComp.text = textData.textContent; textComp.fontSize = textData.fontSize; } else { // 可选:调用基类逻辑处理其他类型 base.CreateMarkerObject(data); } } }
2. 解决Json多态反序列化问题
Unity原生JsonUtility不支持多态类型反序列化——即便JSON包含子类字段,反序列化为MarkerData后会丢失子类信息,类型被截断为基类。解决办法是给MarkerData添加类型标识,手动处理子类反序列化:
第一步:给数据类添加类型标识
public enum MarkerType { Base, Text, Image, Object3D } [Serializable] public class MarkerData { public MarkerType type; public Vector3 pos; // 基类通用字段 } [Serializable] public class TextMarkerData : MarkerData { public string textContent; public int fontSize; // 文本标记专属字段 }
第二步:修改JSON解析逻辑,手动转换子类
修改NodeParser的解析方法,先读取类型标识,再针对性反序列化为对应子类:
public class NodeParser : MonoBehaviour { public List<MarkerData> markers = new List<MarkerData>(); public void CreateMarkersFromJSON() { string jsonPath = Path.Combine(Application.streamingAssetsPath, "MarkerData.json"); string json = File.ReadAllText(jsonPath); // 先解析临时容器,获取每个标记的类型信息 var tempCollection = JsonUtility.FromJson<MarkerCollectionTemp>(json); markers.Clear(); foreach (var tempData in tempCollection.markers) { MarkerData markerData = null; // 根据类型反序列化为对应子类 switch (tempData.type) { case MarkerType.Text: markerData = JsonUtility.FromJson<TextMarkerData>(JsonUtility.ToJson(tempData)); break; case MarkerType.Image: markerData = JsonUtility.FromJson<ImageMarkerData>(JsonUtility.ToJson(tempData)); break; case MarkerType.Object3D: markerData = JsonUtility.FromJson<Object3DMarkerData>(JsonUtility.ToJson(tempData)); break; default: markerData = tempData; break; } if (markerData != null) { CreateMarkerObject(markerData); markers.Add(markerData); } } } // 临时容器类,用于先读取类型标识 [Serializable] private class MarkerCollectionTemp { public List<MarkerData> markers; } public virtual void CreateMarkerObject(MarkerData data) { // 基类通用创建逻辑:比如生成基础标记对象 GameObject markerObj = new GameObject("BaseMarker"); markerObj.transform.position = data.pos; } }
3. 用工厂模式优化类型分发(可选,避免臃肿switch)
如果标记类型较多,switch会变得冗余,可使用工厂模式注册每种类型的创建逻辑:
public static class MarkerFactory { private static readonly Dictionary<MarkerType, Action<MarkerData>> _creators = new(); // 注册每种标记的创建逻辑 public static void RegisterCreator(MarkerType type, Action<MarkerData> creator) { if (!_creators.ContainsKey(type)) { _creators.Add(type, creator); } } // 根据类型创建标记 public static void CreateMarker(MarkerData data) { if (_creators.TryGetValue(data.type, out var creator)) { creator.Invoke(data); } else { // 默认创建逻辑 GameObject markerObj = new GameObject("UnknownMarker"); markerObj.transform.position = data.pos; } } }
在派生类初始化时注册专属逻辑:
public class TextNodeParser : MonoBehaviour { private void Awake() { MarkerFactory.RegisterCreator(MarkerType.Text, data => { if (data is TextMarkerData textData) { GameObject textMarker = new GameObject("TextMarker"); textMarker.transform.position = textData.pos; var textComp = textMarker.AddComponent<TextMeshPro>(); textComp.text = textData.textContent; textComp.fontSize = textData.fontSize; } }); } }
此时NodeParser的CreateMarkerObject可简化为:
public virtual void CreateMarkerObject(MarkerData data) { MarkerFactory.CreateMarker(data); }
关键注意事项
- 所有自定义数据类必须标记
[Serializable],否则JsonUtility无法解析。 - 若需更灵活的多态序列化,可考虑使用Newtonsoft.Json for Unity等第三方库,但原生类型标识方案已满足大多数需求。
- 基类专注通用逻辑,派生类处理特定类型细节,避免代码重复。
内容的提问来源于stack exchange,提问作者Adam L.
相关产品推荐
相关产品推荐

