gpt4 book ai didi

c# - 如何在序列化 OData 响应时忽略 Null 值

转载 作者:行者123 更新时间:2023-12-01 22:44:04 28 4
gpt4 key购买 nike

我需要从响应中完全省略空值字段。我可以通过修改正常 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; }
}

值 Controller :

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”不要出现在第二个项目中(因为它的值为空)。我需要在我的响应中实现它,我不希望用户只查询非空值。

最佳答案

ODataLib v7由于依赖注入(inject) (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...

我们可以全局重写 DefaultODataSerializer,以便使用以下步骤从所有实体和复杂值序列化输出中省略 null 值:

  1. 定义自定义序列化程序,该序列化程序将省略具有空值的属性

Inherit from 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;
}
}
  • 定义一个提供程序来确定何时使用我们的自定义序列化程序

    Inherit from 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);
    }
    }
  • 现在我们需要将其注入(inject)到您的容器生成器中。

    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<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"
    }
    ]
    }
    <小时/>

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

    注意:此时,必须使用此技术来覆盖任何这些默认服务:(如此处定义 OData.Net - Dependency Injection Support

    Service                     Default Implementation      Lifetime    Prototype?--------------------------  --------------------------  ----------  ---------IJsonReaderFactory          DefaultJsonReaderFactory    Singleton   NIJsonWriterFactory          DefaultJsonWriterFactory    Singleton   NODataMediaTypeResolver      ODataMediaTypeResolver      Singleton   NODataMessageReaderSettings  ODataMessageReaderSettings  Scoped      YODataMessageWriterSettings  ODataMessageWriterSettings  Scoped      YODataPayloadValueConverter  ODataPayloadValueConverter  Singleton   NIEdmModel                   EdmCoreModel.Instance       Singleton   NODataUriResolver            ODataUriResolver            Singleton   NUriPathParser               UriPathParser               Scoped      NODataSimplifiedOptions      ODataSimplifiedOptions      Scoped      Y

    UPDATE: How to handle lists or complex types

    Another common scenario is to exclude complex types from the output if all of their properties are null, especially now that we do not include the null properties. We can override the WriteObjectInline method in the IngoreNullEntityPropertiesSerializer for this:

        public override void WriteObjectInline(object graph, IEdmTypeReference expectedType, Microsoft.OData.ODataWriter writer, ODataSerializerContext writeContext)
    {
    if (graph != null)
    {
    // special case, nullable Complex Types, just skip them if there is no value to write
    if (expectedType.IsComplex() && graph.GetType().GetProperty("Instance")?.GetValue(graph) == null
    && (bool?)graph.GetType().GetProperty("UseInstanceForProperties")?.GetValue(graph) == true)
    {
    // skip properties that are null, especially if they are wrapped in generic types or explicitly requested by an expander
    }
    else
    {
    base.WriteObjectInline(graph, expectedType, writer, writeContext);
    }
    }
    }

    Q: And if we need to omit null list properties as well?

    如果您想使用相同的逻辑来排除所有列表,如果它们为空,那么您可以删除 expectedType.IsComplex() 子句:

                // special case, nullable Complex Types, just skip them if there is no value to write
    if (graph.GetType().GetProperty("Instance")?.GetValue(graph) == null
    && (bool?)graph.GetType().GetProperty("UseInstanceForProperties")?.GetValue(graph) == true)
    {
    // skip properties that are null, especially if they are wrapped in generic types or explicitly requested by an expander
    }

    我不建议对导航属性列表使用此方法,只有在 $expand 子句中或通过其他基于约定的逻辑明确请求时,导航属性才会包含在输出中你可能会做同样的事情。输出中的空或 null 数组对于某些客户端逻辑可能很重要,可以确认所请求的属性数据已加载但没有数据可返回。

    关于c# - 如何在序列化 OData 响应时忽略 Null 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24863111/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com