How to Ignore Null values while serializing OData response

前端 未结 3 813
甜味超标
甜味超标 2021-01-03 05:27

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 respo

3条回答
  •  醉梦人生
    2021-01-03 05:35

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

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

    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...

    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. Define your custom Serializer that will omit properties with null values

      Inherit from Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSerializer

      /// 
      /// OData Entity Serilizer that omits null properties from the response
      /// 
      public class IngoreNullEntityPropertiesSerializer : ODataResourceSerializer
      {
          public IngoreNullEntityPropertiesSerializer(ODataSerializerProvider provider)
              : base(provider) { }
      
          /// 
          /// Only return properties that are not null
          /// 
          /// The EDM structural property being written.
          /// The context for the entity instance being written.
          /// The property be written by the serilizer, a null response will effectively skip this property.
          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;
          }
      }
      
    2. Define a Provider that will determine when to use our custom Serializer

      Inherit from Microsoft.AspNet.OData.Formatter.Serialization.DefaultODataSerializerProvider

      /// 
      /// Provider that selects the IngoreNullEntityPropertiesSerializer that omits null properties on resources from the response
      /// 
      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);
          }
      }
      
    3. Now we need to Inject this into your Container Builder.

      the specifics of this will vary depending on your version of .Net, for many older projects this will be where you are mapping the ODataServiceRoute, this will usually be located in your startup.cs or 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(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"
       }
      ]
    }
    

    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
    

提交回复
热议问题