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

2023-11-22

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

值控制器:

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


In ODataLib v7由于依赖注入(DI),围绕这些定制的事情发生了巨大的变化

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

如果您有微软.OData.Corenuget 软件包 v7 或更高版本则这适用于您:)。如果您仍在使用旧版本,请使用 @stas-natalenko 提供的代码,但请不要停止从 ODataController 继承...

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

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

继承自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;
        }
    }
  1. 定义一个 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);
        }
    }
    
  2. 现在我们需要将其注入到您的容器生成器中。

    具体细节将根据您的 .Net 版本而有所不同,对于许多较旧的项目,这将是您映射 ODataServiceRoute 的位置,通常位于您的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"
   }
  ]
}

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

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



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
  

更新:如何处理列表或复杂类型

另一种常见的情况是,如果复杂类型的所有属性均为 null,则从输出中排除复杂类型,尤其是现在我们不包含 null 属性。我们可以重写WriteObjectInline方法中的IngoreNullEntityPropertiesSerializer为了这:

    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:如果我们还需要省略空列表属性呢?

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

            // 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 数组对于某些客户端逻辑可能很重要,可以确认所请求的属性数据已加载但没有数据可返回。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在序列化 OData 响应时忽略 Null 值 的相关文章

  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 匿名类上的 NotSerializedException

    我有一个用于过滤项目的界面 public interface KeyValFilter extends Serializable public static final long serialVersionUID 7069537470113
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 在 MySQL 中使用 COUNT 时如何返回 0 而不是 null

    我使用此查询返回存储在 sTable 中的歌曲列表以及存储在 sTable2 中的总项目数 SQL queries Get data to display sQuery SELECT SQL CALC FOUND ROWS str repl
  • 使用 Jade 评估自定义 javascript 方法 (CircularJSON)

    我想通过 Jade 将一个对象解析为客户端 JavaScript 通常这会起作用 script var object JSON parse JSON stringify object but my object is circular ht
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l

随机推荐

  • 使用 NLTK 提取关系

    这是一个我的问题的后续行动 我正在使用 nltk 来解析人 组织及其关系 使用这个例子 我能够创造出大量的人和组织 但是 我在 nltk sem extract rel 命令中收到错误 AttributeError Tree object
  • 如何在 Linux 上从 python 调用 Wine dll?

    我正在 Linux 中编写 python 脚本 需要调用 Wine 中可用的一些 Windows 函数 具体来说 AllocateAndInitializeSid and LookupAccountSidW 以确定谁登录到远程 Window
  • 在 ASP.NET MVC 中将 .html 文件呈现为视图

    我希望在我的 ASP NET MVC 项目中将 html 文件与其他 cshtml 视图一起用作视图 这样做的主要原因是 在我的操作中 html 文件受到与其他视图所遵守的相同的自定义安全规则的约束 我不想使用 cshtml 或 aspx
  • 我可以告诉编译器考虑关于返回值的控制路径关闭吗?

    假设我有以下功能 Thingy getThingy int id for int i 0 i lt something i normal execution guarantees that the Thingy we re looking
  • 具有复杂键的 Spring @Cacheable 仍然执行

    我对 spring 3 1 中 Cacheable 的使用有以下内容 spring
  • 你能在一条语句中访问MySQL中的自动增量值吗?

    我有一个 MySQL 数据库 其中包含一个用户表 表的主键是 userid 设置为自增字段 我想做的是 当我将新用户插入表中时 使用自动增量在不同字段 default assignment 的 userid 字段中创建的相同值 e g 我想
  • C++:声明一个全局类并从其他类访问它?

    我有一个类应该从 main 全局声明并从程序中的其他声明的类访问 我该怎么做 class A int i int value return i class B global A a or extern int calc return a v
  • Python 中 Tesseract OCR 的 UnicodeDecodeError

    我正在尝试使用 Python 中的 Tesseract OCR 从图像文件中提取文本 但我遇到了一个错误 我可以弄清楚如何处理它 我的所有环境都很好 因为我用 python 中的 ocr 测试了一些示例图像 这是代码 from PIL im
  • 更改 MySQL 错误消息语言

    我的 MySQL 错误语言有问题 当查询出现错误时 该错误不是英文的 就像下面的消息 Le champ id ne peut tre vide null 我已经运行此查询来更改语言 但仍然出现相同的错误 SET lc messages en
  • 本地 ffmpeg 输出到 S3 Bucket

    这是我的设置 我有一台运行 ffmpeg 的本地 PC 输出配置为 h 264 和 aac 以及在AWS创建的S3存储桶 我需要做的是 使用 ffmpeg local 输出将文件直接上传到 s3 存储桶 附 计划将 s3 存储桶与 clou
  • DOM/Javascript:获取标签后的文本

    如何获取 html 文档中标签后面的文本 there p a hello a there p 我发现有一种方法可以用 xpath 做到这一点 从下一个标签获取文本 但我没有使用 xpath 并且希望不必为此而开始 我意识到我可以获取 p 标
  • 如何在Java.time中将LocalDateTime的精度设置为纳秒?

    根据java time 文档 java time应该能够以纳秒精度呈现 LocalDateTime 或 LocalTime 但是当我运行时LocalDateTime now 打印出来 只显示3位数字 而不是9位 像这样 2016 08 11
  • 怎么去掉这些点啊!

    我正在 VS2008 的一个项目上编码 我试图按 CTR R E 将字段封装为属性 resharper 的功能 但我摸索着 现在我到处都是点 我有空格 我不知道如何摆脱它 例如 using System Namespace EgNamesp
  • C++ 从 hBitmap 获取 RGB

    使用位图对我来说是非常陌生的 所以我一直在努力阅读我读过的在线教程和策略 基本上我的目标是扫描屏幕以获取特定的 RGB 值 我相信执行此操作的步骤是在 hBitmap 中捕获屏幕 然后从中生成一个我可以扫描的 RGB 值数组 我最初从 Ge
  • 空基类优化

    引用 C 标准第 1 8 节中的两句话 对象是一个存储区域 基类子对象的大小可能为零 我不认为存储区域的大小可以为零 这意味着某些基类子对象实际上并不是对象 这些说法如何共存 对于 区域 的定义进行哲学争论是没有必要的 1 8 5 说 除非
  • 使一个条形的边框比其他条形更暗

    我在 ggplot2 中创建了一个条形图 其中 3 个条代表做出 3 个选择中的 1 个的概率 我想添加一个bolded显示正确响应的条形周围的边框 我还没有找到办法做到这一点 我可以改变所有条形的颜色 但不仅仅是一个 所附图像显示了我生成
  • 为什么 Spark Cassandra 连接器会失败并出现 NoHostAvailableException?

    我在让 Spark Cassandra Connector 在 Scala 中工作时遇到问题 我正在使用这些版本 斯卡拉2 10 4 火花核心 1 0 2 cassandra thrift 2 1 0 我安装的cassandra是v2 1
  • 升压 asio io_service.run()

    我只是想看一下 asio聊天服务器示例 我的问题是关于他们的使用情况io service run 功能 的文档io service run 函数说 run 函数会阻塞 直到所有工作都完成并且没有 更多处理程序被调度 或者直到 io serv
  • 使用 g++ 与 pragma 链接

    在 Visual C 中 可以通过以下方式链接到代码本身中的库 pragma comment lib libname lib g 中可能有类似的东西吗 The 升压配置库对自动链接有一些支持 使用特定编译器的相关编译器特定代码 然而 文档指
  • 如何在序列化 OData 响应时忽略 Null 值

    我需要从响应中完全省略空值字段 我可以通过修改正常 webapi 响应的 JsonFormatter 序列化设置来做到这一点 config Formatters JsonFormatter SerializationSettings Nul