C#中如何处理支持两种不同类型取值的JSON字段
处理COCO格式多态Segmentation字段的实现方案
不需要使用object类型声明字段,直接通过自定义JSON转换器根据token类型自动匹配反序列化目标即可,全程保持类型安全,且完全兼容官方COCO标注格式规范,实现步骤如下:
1. 定义类型层级
首先定义专用抽象基类作为segmentation字段的静态类型,再分别实现两种格式的派生类,避免类型混淆:
public abstract class Segmentation { // 可在此处添加公共辅助方法,比如掩码转换、面积计算等逻辑 } // 多边形格式对应实现 public class PolygonSegmentation : Segmentation { public List<List<float>> Polygons { get; set; } = []; } // RLE格式对应实现,复用已有的RLE结构定义 public class RleSegmentation : Segmentation { [JsonProperty("size")] public List<int>? Size { get; set; } [JsonProperty("counts")] public List<int>? Counts { get; set; } }
在标注类中,将segmentation字段声明为Segmentation类型,同时挂载自定义转换器:
public class CocoAnnotation { [JsonProperty("id")] public int Id { get; set; } [JsonProperty("image_id")] public int ImageId { get; set; } [JsonProperty("category_id")] public int CategoryId { get; set; } [JsonProperty("segmentation")] [JsonConverter(typeof(SegmentationConverter))] public Segmentation Segmentation { get; set; } = null!; [JsonProperty("area")] public float Area { get; set; } [JsonProperty("bbox")] public List<float> Bbox { get; set; } = []; [JsonProperty("iscrowd")] public int IsCrowd { get; set; } }
2. 实现自定义JSON转换器
核心判断逻辑:反序列化时先读取当前JSON token类型,数组类型匹配多边形格式,对象类型匹配RLE格式;序列化时根据传入的具体派生类型执行对应序列化逻辑。
Newtonsoft.Json 版本实现
public class SegmentationConverter : JsonConverter<Segmentation> { public override Segmentation? ReadJson(JsonReader reader, Type objectType, Segmentation? existingValue, bool hasExistingValue, JsonSerializer serializer) { var token = JToken.Load(reader); return token.Type switch { JTokenType.Array => new PolygonSegmentation { Polygons = token.ToObject<List<List<float>>>(serializer) ?? [] }, JTokenType.Object => token.ToObject<RleSegmentation>(serializer), _ => throw new JsonSerializationException($"非法的segmentation格式,token类型: {token.Type}") }; } public override void WriteJson(JsonWriter writer, Segmentation? value, JsonSerializer serializer) { switch (value) { case PolygonSegmentation polygon: serializer.Serialize(writer, polygon.Polygons); break; case RleSegmentation rle: serializer.Serialize(writer, rle); break; default: throw new JsonSerializationException($"未知的Segmentation类型: {value?.GetType().FullName}"); } } }
System.Text.Json 版本实现
public class SegmentationSystemTextJsonConverter : JsonConverter<Segmentation> { public override Segmentation? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch { JsonTokenType.StartArray => new PolygonSegmentation { Polygons = JsonSerializer.Deserialize<List<List<float>>>(ref reader, options) ?? [] }, JsonTokenType.StartObject => JsonSerializer.Deserialize<RleSegmentation>(ref reader, options), _ => throw new JsonException($"非法的segmentation格式,token类型: {reader.TokenType}") }; } public override void Write(Utf8JsonWriter writer, Segmentation value, JsonSerializerOptions options) { switch (value) { case PolygonSegmentation polygon: JsonSerializer.Serialize(writer, polygon.Polygons, options); break; case RleSegmentation rle: JsonSerializer.Serialize(writer, rle, options); break; default: throw new JsonException($"未知的Segmentation类型: {value.GetType().FullName}"); } } }
实现说明
- 逻辑完全对齐COCO官方规范:
iscrowd=0时segmentation固定为多边形数组,iscrowd=1时固定为RLE对象,类型判断不会出现匹配错误。 - 不需要额外添加类型鉴别字段,序列化后的结果和官方COCO格式完全一致,不会出现兼容性问题。
- 字段全程使用强类型声明,完全规避
object类型带来的类型安全问题,后续扩展业务逻辑时可直接在基类添加抽象方法,减少冗余的类型判断代码。
内容的提问来源于stack exchange,提问作者Roland Deschain
相关产品推荐
相关产品推荐

