我要求完全忽略响应中的空值字段。 我可以通过修改正常webapi响应的JsonFormatter序列化设置来完成此操作。
config.Formatters.JsonFormatter.SerializationSettings
.NullValueHandling = NullValueHandling.Ignore;
但是,切换到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());
}
物品型号:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string OptionalField { get; set; }
}
ValuesController:
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);
}
}
以下是我获得的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
}
]
}
但是我不需要响应中存在空值的元素 - 在下面的响应中,我需要“OptionalField”不存在于第二个项目中(因为它的值为null)。我需要在我的响应中实现它,我不希望用户只查询非空值。
答案 0 :(得分:2)
我知道它看起来并不合乎逻辑,但只需将DefaultODataSerializerProvider和DefaultODataDeserializerProvider添加到Formatters列表中就可以了:
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);
<强>更新强>
由于全局格式化程序修改对我来说无法正常工作,我选择了另一种方式。 首先,我离开了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());
}
}
格式化属性将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);
}
}
最后,自定义序列化程序使用空值过滤结构属性:
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;
}
}
在我看来,这似乎不是最好的解决方案,但这是我找到的解决方案。
答案 1 :(得分:0)
所有方法都相同,我对webapiconfig进行了更改
var odataFormatters = ODataMediaTypeFormatters.Create(new CustomODataSerializerProvider(), new DefaultODataDeserializerProvider());
config.Formatters.InsertRange(0, odataFormatters);
这有助于我解决结果
答案 2 :(得分:0)
在ODataLib v7中,由于依赖注入(DI)的影响,这些类型的自定义发生了巨大变化
此建议适用于已升级到ODataLib v7且可能已实施了先前接受的答案的任何人。
如果您拥有Microsoft.OData.Core nuget软件包v7或更高版本,则适用于您:)。如果您仍在使用旧版本,请使用@ stas-natalenko提供的代码,但请不要停止从ODataController继承...
我们可以全局重写DefaultODataSerializer,以便使用以下步骤从所有Entity和Complex值序列化输出中省略空值:
定义您的自定义序列化程序,它将忽略具有空值的属性
继承自
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;
}
}
定义将确定何时使用我们的自定义序列化程序的提供程序
继承自
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);
}
}
现在我们需要将其注入到您的Container Builder中。
具体取决于您的.Net版本,对于许多较旧的项目,这是您映射ODataServiceRoute的位置,通常位于您的
startup.cs
或WebApiConfig.cs
< / p>
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));
在这里,重新执行查询,您将获得以下信息:
{
"odata.metadata":"http://localhost:28776/api/$metadata#Values",
"value":[
{
"Id":1,
"Name":"name1",
"OptionalField":"Value Present"
},
{
"Id":3,
"Name":"name2"
}
]
}
这是一个非常方便的解决方案,可以大大减少许多基于OData Services的数据输入应用程序上的数据消耗
注意:目前,必须使用此技术来覆盖以下任何默认服务:(如OData.Net - Dependency Injection Support定义
Service Default Implementation Lifetime Prototype? -------------------------- -------------------------- ---------- --------- IJsonReaderFactory DefaultJsonReaderFactory Singleton N IJsonWriterFactory DefaultJsonWriterFactory Singleton N ODataMediaTypeResolver ODataMediaTypeResolver Singleton N ODataMessageReaderSettings ODataMessageReaderSettings Scoped Y ODataMessageWriterSettings ODataMessageWriterSettings Scoped Y ODataPayloadValueConverter ODataPayloadValueConverter Singleton N IEdmModel EdmCoreModel.Instance Singleton N ODataUriResolver ODataUriResolver Singleton N UriPathParser UriPathParser Scoped N ODataSimplifiedOptions ODataSimplifiedOptions Scoped Y