序列化OData响应时如何忽略Null值 [英] How to Ignore Null values while serializing OData response

查看:160
本文介绍了序列化OData响应时如何忽略Null值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要完全忽略响应中的空值字段. 我可以通过修改普通的webapi响应的JsonFormatter序列化设置来完成此操作.

I've a requirement to omit the null valued fields from the response altogether. I can do this by modifying the JsonFormatter Serialization Setting for a normal webapi response.

config.Formatters.JsonFormatter.SerializationSettings
      .NullValueHandling = NullValueHandling.Ignore;

但是一旦我切换到OData,那似乎就不起作用了.

But that does not seem to work once i switch to OData.

这是我的文件: WebApi.config:

Here are my files: 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:

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.

Here is the response I get for 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).我需要在响应中实现它,我不希望用户仅查询非null值.

But I do not need the elements with null values present in the response - in the response below, I need the "OptionalField" not to be present in the second item (As its value is null). I need to achieve it in my response, I do not want the users to query for non-null values only.

推荐答案

ODataLib v7 借助Depencency Injection(DI)大大改变了此类自定义设置

In ODataLib v7 things changed drastically around these sorts of customisations thanks to Depencency Injection (DI)

此建议适用于已升级到ODataLib v7的任何人,他们可能已经实现了先前接受的答案.

This advice is for anyone who has upgraded to ODataLib v7, who may have implemented the previously accepted answers.

如果您具有 Microsoft.OData.Core nuget包v7或稍后,这适用于您:).如果您仍在使用旧版本,请使用@ stas-natalenko提供的代码,但请不要停止从ODataController继承...

If you have the Microsoft.OData.Core nuget package v7 or later then this applies to you :). If you are still using older versions then use the code provided by @stas-natalenko but please DO NOT stop inheriting from ODataController...

我们可以全局覆盖DefaultODataSerializer,以便使用以下步骤从所有Entity和Complex值序列化输出中省略空值:

We can globally override the DefaultODataSerializer so that null values are omitted from all Entity and Complex value serialized outputs using the following steps:

  1. 定义您的自定义序列化程序,它将忽略具有空值的属性

  1. Define your custom Serializer that will omit properties with null values

继承自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;
    }
}

  • 定义一个提供程序,该提供程序将确定何时使用我们的自定义序列化程序

  • Define a Provider that will determine when to use our custom Serializer

    继承自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中.

  • Now we need to Inject this into your Container Builder.

    具体细节将取决于您的.Net版本,对于许多较旧的项目,这将是您映射ODataServiceRoute的位置,通常位于您的startup.csWebApiConfig.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));
    

  • 在这里,重新执行查询,您将获得以下信息:

    And there you have it, re-exeecute your query and you should get the following:

    {
     "odata.metadata":"http://localhost:28776/api/$metadata#Values",
     "value":[
       {
         "Id":1,
         "Name":"name1",
         "OptionalField":"Value Present"
       },
       {
         "Id":3,
         "Name":"name2"
       }
      ]
    }
    


    这是一个非常方便的解决方案,可以显着减少许多基于OData Services的数据输入应用程序上的数据消耗

    This is a very handy solution that can significantly reduce the data consumption on many data entry applications based on OData Services

    注意:在此时间点上,必须使用此技术来覆盖以下任何默认服务:(如此处

    NOTE: At this point in time, this technique must be used to override any of these default services: (as defined here 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
    

    这篇关于序列化OData响应时如何忽略Null值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆