WebAPI 2 OData 3项目中C# Uri类型JSON序列化异常求助
解决WebAPI 2 OData 3中Uri类型序列化成分段数组的问题
我之前在处理OData 3项目时遇到过完全相同的问题——默认的OData序列化逻辑会把Uri类型解析成包含Segments的对象,而不是直接输出完整的URL字符串。结合你尝试过调整序列化设置、更换契约序列化器都无效的情况,这里有几个针对性的解决方案:
方案1:修改Edm模型,将Uri属性标记为字符串类型(推荐)
OData之所以会把Uri拆成段数组,是因为它默认把Uri类型识别为实体引用链接(EntityReferenceLink),而非普通字符串。我们可以在构建Edm模型时,显式指定Url属性的类型为字符串,强制OData按字符串序列化:
- 在你的WebApi配置类(比如
WebApiConfig.cs)中,找到构建OData模型的代码,添加显式的属性类型配置:
public static IEdmModel GetEdmModel() { var builder = new ODataConventionModelBuilder(); builder.EntitySet<MyObject>("MyObjects"); // 关键:将Url属性标记为字符串类型 builder.EntityType<MyObject>() .Property(x => x.Url) .AsPrimitive(typeof(string)); // 强制OData把Uri当作字符串处理 return builder.GetEdmModel(); }
- 确保你的OData路由使用这个自定义模型:
config.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: "odata", model: GetEdmModel() );
这个方法不需要自定义序列化器,直接通过模型配置修正行为,是最简洁的解决方案。
方案2:自定义OData序列化器,重写Uri的序列化逻辑
如果方案1无法满足需求(比如需要保留Uri类型的Edm元数据定义),可以通过自定义序列化器来完全控制Uri的输出格式:
步骤1:创建自定义Uri序列化器
public class UriSerializer : ODataPrimitiveSerializer { public override ODataPrimitiveValue CreateODataPrimitiveValue(object graph, IEdmPrimitiveTypeReference primitiveType, ODataSerializerContext writeContext) { // 当处理的对象是Uri时,直接返回其完整字符串 if (graph is Uri uri) { return new ODataPrimitiveValue(uri.ToString()); } // 其他类型沿用默认逻辑 return base.CreateODataPrimitiveValue(graph, primitiveType, writeContext); } }
步骤2:创建自定义序列化器提供者
public class CustomODataSerializerProvider : DefaultODataSerializerProvider { private readonly UriSerializer _uriSerializer; public CustomODataSerializerProvider() { _uriSerializer = new UriSerializer(); } // 针对Uri类型返回自定义序列化器 public override ODataSerializer GetODataSerializer(Type type) { if (type == typeof(Uri)) { return _uriSerializer; } return base.GetODataSerializer(type); } }
步骤3:注册自定义序列化器提供者
在WebApi配置中,将自定义提供者绑定到OData路由:
config.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: "odata", model: GetEdmModel(), serializerProvider: new CustomODataSerializerProvider() );
方案3:包装Uri属性为字符串(最简单的临时方案)
如果不想修改OData配置或编写自定义序列化器,可以直接在MyObject类中把Uri属性包装成字符串类型,内部维护Uri实例:
public class MyObject { private Uri _url; // 对外暴露字符串类型的Url属性 public string Url { get => _url?.ToString(); set => _url = string.IsNullOrEmpty(value) ? null : new Uri(value); } public string Name { get; set; } public string ID { get; set; } }
这个方法不需要任何OData配置修改,直接通过实体类调整解决问题,适合快速验证或小型项目。
验证效果
无论使用哪种方案,重新运行项目后,你的OData接口都会输出期望的JSON格式:
{ "odata.metadata":"http://localhost:51607/odata/$metadata#MyObjects", "value":[ { "Url":"http://www.webapp.com/sites/page.html", "Name":"123rwe", "ID":"asdf" } ] }
内容的提问来源于stack exchange,提问作者Andrew Eberhard
相关产品推荐
相关产品推荐

