OData V4结合Web API 2序列化含Object属性的POCO列表失败
解决OData V4序列化含object属性的POCO列表问题
我之前也碰到过一模一样的问题!用Web API 2 + OData V4的时候,只要POCO里带object类型的属性,集合序列化就容易出问题,尤其是你这种Bar类里的Value存基础类型但声明成object的情况。核心原因就是OData是元数据驱动的,它没法自动推断object属性的实际类型,导致序列化时卡壳。下面给你几个可行的解决办法:
方法1:把Bar标记为OData开放类型
开放类型专门用来处理带有动态属性的类,刚好适配object类型的场景。你只需要给Bar类加上[OpenType]特性就行:
using Microsoft.OData.Edm; [OpenType] class Bar { public string Name {get; set;} public int? Group {get; set;} public object Value {get; set;} }
如果是用ODataConventionModelBuilder构建EDM模型,它会自动识别这个特性,生成对应的开放类型元数据,不用额外配置。
方法2:显式注册Value属性的所有已知类型
如果你能确定Value只会用到特定的基础类型(比如string、int、bool这些),可以在构建EDM模型时,给Bar的Value属性提前注册这些类型,让OData知道可能的类型范围:
public static IEdmModel GetEdmModel() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Foo>("Foos"); // 获取Bar的实体类型配置 var barType = builder.EntityType<Bar>(); // 把Value可能用到的所有基础类型加进去 barType.Property(b => b.Value).HasDynamicProperties( typeof(string), typeof(int), typeof(bool), typeof(DateTime), typeof(decimal) ); return builder.GetEdmModel(); }
这样OData序列化时就能根据Value的实际类型匹配对应的元数据,不会再出错。
方法3:自定义OData序列化器(适合复杂场景)
如果上面两种方法都满足不了你的需求,可以自己写个序列化器,手动处理Bar类型的序列化逻辑:
首先写自定义序列化器:
public class BarSerializer : ODataEntityTypeSerializer { public BarSerializer(ODataSerializerProvider serializerProvider) : base(serializerProvider) { } public override ODataValue CreateODataValue(object graph, IEdmTypeReference expectedType, ODataSerializerContext writeContext) { if (graph is Bar bar) { var entityType = expectedType.AsEntityType(); var oDataEntity = new ODataResource(); // 先处理Name和Group这两个常规属性 oDataEntity.Properties.Add(new ODataProperty { Name = "Name", Value = bar.Name }); oDataEntity.Properties.Add(new ODataProperty { Name = "Group", Value = bar.Group }); // 手动处理Value属性,根据实际类型序列化 if (bar.Value != null) { var valueType = bar.Value.GetType(); var valueSerializer = SerializerProvider.GetEdmTypeSerializer(valueType); var valueOData = valueSerializer.CreateODataValue(bar.Value, EdmCoreModel.Instance.GetEdmPrimitiveTypeReference((EdmPrimitiveTypeKind)Enum.Parse(typeof(EdmPrimitiveTypeKind), valueType.Name)), writeContext); oDataEntity.Properties.Add(new ODataProperty { Name = "Value", Value = valueOData }); } return oDataEntity; } return base.CreateODataValue(graph, expectedType, writeContext); } }
然后注册这个序列化器:
public class CustomODataSerializerProvider : DefaultODataSerializerProvider { private readonly BarSerializer _barSerializer; public CustomODataSerializerProvider() { _barSerializer = new BarSerializer(this); } public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType) { if (edmType.IsEntity() && edmType.AsEntityType().Name == nameof(Bar)) { return _barSerializer; } return base.GetEdmTypeSerializer(edmType); } }
最后在Web API配置里替换默认的序列化器:
config.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: null, model: GetEdmModel(), batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer), pathHandler: new DefaultODataPathHandler(), routingConventions: ODataRoutingConventions.CreateDefault(), serializerProvider: new CustomODataSerializerProvider() );
这三种方法里,方法1最简单,适合大多数场景;方法2适合你能明确Value所有可能类型的情况;方法3最灵活,适合复杂的自定义需求。
内容的提问来源于stack exchange,提问作者Victor Wilson
相关产品推荐
相关产品推荐

