Pav*_*van 5 c# serialization json odata asp.net-web-api
我需要完全从响应中省略空值字段。我可以通过修改普通 webapi 响应的 JsonFormatter 序列化设置来做到这一点。
config.Formatters.JsonFormatter.SerializationSettings
.NullValueHandling = NullValueHandling.Ignore;
Run Code Online (Sandbox Code Playgroud)
但是一旦我切换到OData.
这是我的文件:WebApi.config:
public static void Register(HttpConfiguration config)
{
var builder = new ODataConventionModelBuilder();
var workerEntitySet = builder.EntitySet<Item>("Values");
config.Routes.MapODataRoute("Default", "api", builder.GetEdmModel());
}
Run Code Online (Sandbox Code Playgroud)
商品型号:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string OptionalField { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
值控制器:
public class ValuesController : EntitySetController<Item, int>
{
public static List<Item> items = new List<Item>()
{
new Item { Id = 1, Name = "name1", OptionalField = "Value Present" },
new Item { Id = 3, Name = "name2" }
};
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public override IQueryable<Item> Get()
{
return items.AsQueryable();
}
[Queryable]
protected override Item GetEntityByKey(int id)
{
return items.Single(i => i.Id == id);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的 GET 响应:api/Values。
{
"odata.metadata":"http://localhost:28776/api/$metadata#Values",
"value":[
{
"Id":1,
"Name":"name1",
"OptionalField":"Value Present"
},
{
"Id":3,
"Name":"name2",
"OptionalField":null
}
]
}
Run Code Online (Sandbox Code Playgroud)
但是我不需要响应中存在空值的元素 - 在下面的响应中,我需要“OptionalField”不要出现在第二项中(因为它的值为空)。我需要在我的响应中实现它,我不希望用户只查询非空值。
在ODataLib v7 中,由于依赖注入 (DI),围绕这些类型的定制发生了巨大变化
此建议适用于升级到 ODataLib v7 的任何人,他们可能已经实施了之前接受的答案。
如果您拥有Microsoft.OData.Core nuget 包 v7 或更高版本,则这适用于您:)。如果您仍在使用旧版本,请使用@stas-natalenko 提供的代码,但请不要停止从 ODataController 继承...
我们可以全局覆盖 DefaultODataSerializer,以便使用以下步骤从所有实体和复杂值序列化输出中省略空值:
定义您的自定义序列化程序,它将省略具有空值的属性
继承自
Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer
/// <summary>
/// OData Entity Serilizer that omits null properties from the response
/// </summary>
public class IngoreNullEntityPropertiesSerializer : ODataResourceSerializer
{
public IngoreNullEntityPropertiesSerializer(ODataSerializerProvider provider)
: base(provider) { }
/// <summary>
/// Only return properties that are not null
/// </summary>
/// <param name="structuralProperty">The EDM structural property being written.</param>
/// <param name="resourceContext">The context for the entity instance being written.</param>
/// <returns>The property be written by the serilizer, a null response will effectively skip this property.</returns>
public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
return property.Value != null ? property : null;
}
}
Run Code Online (Sandbox Code Playgroud)定义一个 Provider 来决定何时使用我们的自定义序列化程序
继承自
Microsoft.AspNet.OData.Formatter.Serialization.DefaultODataSerializerProvider
/// <summary>
/// Provider that selects the IngoreNullEntityPropertiesSerializer that omits null properties on resources from the response
/// </summary>
public class IngoreNullEntityPropertiesSerializerProvider : DefaultODataSerializerProvider
{
private readonly IngoreNullEntityPropertiesSerializer _entityTypeSerializer;
public IngoreNullEntityPropertiesSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer) {
_entityTypeSerializer = new IngoreNullEntityPropertiesSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
{
// Support for Entity types AND Complex types
if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
return _entityTypeSerializer;
else
return base.GetEdmTypeSerializer(edmType);
}
}
Run Code Online (Sandbox Code Playgroud)现在我们需要将其注入到您的 Container Builder 中。
具体细节取决于您的 .Net 版本,对于许多较旧的项目,这将是您映射 ODataServiceRoute 的位置,这通常位于您的
startup.cs或WebApiConfig.cs
builder => builder
.AddService(ServiceLifetime.Singleton, sp => model)
// Injected our custom serializer to override the current ODataSerializerProvider
// .AddService<{Type of service to Override}>({service lifetime}, sp => {return your custom implementation})
.AddService<Microsoft.AspNet.OData.Formatter.Serialization.ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new IngoreNullEntityPropertiesSerializerProvider(sp));
Run Code Online (Sandbox Code Playgroud)你有它,重新执行你的查询,你应该得到以下信息:
{
"odata.metadata":"http://localhost:28776/api/$metadata#Values",
"value":[
{
"Id":1,
"Name":"name1",
"OptionalField":"Value Present"
},
{
"Id":3,
"Name":"name2"
}
]
}
Run Code Online (Sandbox Code Playgroud)
这是一个非常方便的解决方案,可以显着减少基于 OData 服务的许多数据输入应用程序的数据消耗
注意:此时,必须使用此技术来覆盖这些默认服务中的任何一个:(定义如下 OData.Net - Dependency Injection Support
服务默认实现生命周期原型? -------------------------- ------------------------ -- ---------- --------- IJsonReaderFactory DefaultJsonReaderFactory Singleton N IJsonWriterFactory DefaultJsonWriterFactory Singleton N ODataMediaTypeResolver ODataMediaTypeResolver Singleton N ODataMessageReaderSettings ODataMessageReaderSettings 作用域 Y ODataMessageWriterSettings ODataMessageWriterSettings 范围 Y ODataPayloadValueConverter ODataPayloadValueConverter Singleton N IEdmModel EdmCoreModel.Instance 单例 N ODataUriResolver ODataUriResolver Singleton N UriPathParser UriPathParser 作用域 N ODataSimplifiedOptions ODataSimplifiedOptions 作用域 Y
我知道它看起来不符合逻辑,但只需将 DefaultODataSerializerProvider 和 DefaultODataDeserializerProvider 添加到格式化程序列表中就可以解决我的问题:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//...
var odataFormatters = System.Web.OData.Formatter.ODataMediaTypeFormatters.Create(
System.Web.OData.Formatter.Serialization.DefaultODataSerializerProvider.Instance,
System.Web.OData.Formatter.Deserialization.DefaultODataDeserializerProvider.Instance);
config.Formatters.AddRange(odataFormatters);
Run Code Online (Sandbox Code Playgroud)
更新
由于全局格式化程序修改对我来说无法正常工作,因此我选择了另一种方式。首先,我放弃了 ODataController,并使用自定义 ODataFormatting 属性从 ApiController 继承了我的控制器:
[ODataRouting]
[CustomODataFormatting]
public class MyController : ApiController
{
...
}
public class CustomODataFormattingAttribute : ODataFormattingAttribute
{
public override IList<System.Web.OData.Formatter.ODataMediaTypeFormatter> CreateODataFormatters()
{
return System.Web.OData.Formatter.ODataMediaTypeFormatters.Create(
new CustomODataSerializerProvider(),
new System.Web.OData.Formatter.Deserialization.DefaultODataDeserializerProvider());
}
}
Run Code Online (Sandbox Code Playgroud)
格式化属性将 DefaultODataSerializerProvider 替换为修改后的属性:
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if(edmType.Definition.TypeKind == EdmTypeKind.Entity)
return new CustomODataEntityTypeSerializer(this);
else
return base.GetEdmTypeSerializer(edmType);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,自定义序列化器过滤具有空值的结构属性:
public class CustomODataEntityTypeSerializer : System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer
{
public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider) { }
public override ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, EntityInstanceContext entityInstanceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, entityInstanceContext);
return property.Value != null ? property : null;
}
}
Run Code Online (Sandbox Code Playgroud)
在我看来,这似乎不是最好的解决方案,但这是我找到的解决方案。
| 归档时间: |
|
| 查看次数: |
6154 次 |
| 最近记录: |