【问题标题】:OData serializingOData 序列化
【发布时间】:2014-02-19 16:14:20
【问题描述】:

在过去的 2 个个月,我一直在尝试寻找一种自定义序列化从 OData 控制器返回的实体的解决方案!请帮忙!!!

用例非常简单,为了解决问题,我进一步简化了它。我的模型中的某些实体附加了一些虚拟字段,例如:

public class Customer
{
    public int CustomerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string VirtualField1 { get; set; }
    public string VirtualField2 { get; set; }
    public string VirtualField3 { get; set; }
}

现在,假设客户端已将 VirtualField1 配置为“CompanyName”。
我要做的就是创建一个自定义的 JSON 序列化器和反序列化器:

  1. 任何对客户(当然客户 - 即 - IQueryable)的 GET 请求都将通过此序列化程序,该序列化程序将在每个客户的集合案例。
  2. 任何 POST 请求都将通过相反的替换 - 即 - 将“CompanyName”替换为“VirtualField1”。
    ** 实际的替换逻辑有点复杂,但思路是一样的。

我已经阅读了 google 可以找到的所有内容,但找不到任何可行的示例。 以下是一些链接:
https://aspnetwebstack.codeplex.com/wikipage?title=OData%20formatter%20extensibility
** 当前的 OData API 现在有点不同,但我认为原理是相同的。
customizing odata output from asp.net web api
Using OData in webapi for properties known only at runtime

所有链接(以及我为此找到的任何信息)的共同点是我必须从 DefaultODataSerializerProvider 继承并将其添加到我的格式化程序中:
在 WebApiConfig.cs 上:

       var customFormatters = ODataMediaTypeFormatters.Create(new CustomODataSerilizerProvider(), new CustomODataDeSerilizerProvider());
       config.Formatters.InsertRange(0, customFormatters);

以及实际的提供者和序列化器:

 public class CustomODataSerilizerProvider : DefaultODataSerializerProvider
 {
    public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
    {
        if (edmType.IsEntity())
        {
            return new CustomODataEntityTypeSerializer(edmType.AsEntity(), this);
        }

        return base.GetEdmTypeSerializer(edmType);
    }
 }

** edmType.IsEntity() 对于 IQueryable 结果永远不会为真,因此它永远不会创建具体的序列化程序。如果我强制创建它仍然不会在 CreateEntity (或任何其他创建方法)上中断。

  public class CustomODataEntityTypeSerializer : ODataEntityTypeSerializer
  {
    public CustomODataEntityTypeSerializer(IEdmEntityTypeReference entityType, ODataSerializerProvider serializerProvider)
        : base(serializerProvider)
    {
    }

    public override ODataEntry CreateEntry(SelectExpandNode selectExpandNode, EntityInstanceContext entityInstanceContext)
    {
        var oDataEntry = base.CreateEntry(selectExpandNode, entityInstanceContext);
        return oDataEntry;
    }
 }

如果我将具体的序列化程序更改为从 ODataCollectionSerializer 继承:

  public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
    {

        if (edmType.IsCollection())
        {
            return new CollectionSerilizer(this);
        }

        return base.GetEdmTypeSerializer(edmType);
    }

public class CollectionSerilizer : ODataCollectionSerializer
{
    public CollectionSerilizer(ODataSerializerProvider serializerProvider) : base(serializerProvider)
    {
    }

    public override ODataCollectionValue CreateODataCollectionValue(IEnumerable enumerable, IEdmTypeReference elementType,
        ODataSerializerContext writeContext)
    {
        var oDataCollectionValue = base.CreateODataCollectionValue(enumerable, elementType, writeContext);
        return oDataCollectionValue;
    }

    public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
    {
        base.WriteObject(graph, type, messageWriter, writeContext);
    }
}

它确实在 WriteObject 上的断点处停止,但不起作用并且基础正在抛出: “指定为集合项类型的类型‘Models.Customer’不是原始或复杂的。ODataCollectionWriter 只能写入原始或复杂值的集合。”

另一个有趣的事情是,即使我插入了所谓的默认提供程序:

var customFormatters = ODataMediaTypeFormatters.Create(new DefaultODataSerializerProvider(), new DefaultODataDeserializerProvider());
        config.Formatters.InsertRange(0, customFormatters);

无论他们的位置如何,要么是在开头:

config.Formatters.InsertRange(0, customFormatters);

或在最后:

config.Formatters.AddRange(customFormatters);

OData 功能 - 即 - 例如:在 /odata/Customers?$expand=Images 中的 $exapnd 完全消失并且根本不起作用(这是响应):

 [{"Images":[],"CustomerId":1,"FirstName":"Bla","LastName":"Bla", "VirtualField1":null]

尽管没有添加它们所做的自定义格式化程序,但此实例中的图像扩展。

有什么想法、想法、方向???

【问题讨论】:

  • 你有没有找到答案,我有一个带有自定义集合的类型,我使用自定义 JsonConverter 这适用于 vanilla WebAPI,但是一旦我将 OData 引入混合对象从 API 返回的不能再在我的客户端中反序列化。我几乎没有发现如何让这个工作。如果您找到了解决方案,可以分享一下吗?
  • 不幸的是,我没有,而且据我所知,它不能使用 ODate 完成。究其原因,深层次是OData的序列化机制,与模型深度耦合。因此修改模型会破坏序列化。我认为存在的唯一可能的方法是添加一些过滤器 After 序列化发生。祝你好运,如果你找到方法,请添加一些信息。
  • 我放弃了这种方法。我决定删除 OData 包并坚持使用 vanilla WebAPI,因为创建和使用可跟踪实体对我来说更重要。
  • 祝你好运!使用 API 很好,但是在发送循环依赖对象时要小心,因为 JSON 序列化器在这方面做得不好(A 包含 B 包含 A,如果不处理,基本上会导致无限响应,这显然是避免使用 OData,因为您必须使用 $expand 来获取对象访问器)。
  • 谢谢。顺便说一句:WebAPI 现在使用 Json.NET,它非常擅长处理 ref 循环。

标签: json serialization asp.net-web-api odata


【解决方案1】:

现在回答可能为时已晚,但我注意到微软的 OData 工具包在 OData V1-3 和 OData V4 的序列化方面的可用性存在显着差异。

我认为您使用的是 V4,因为我也尝试过使用 DefaultODataSerializerProvider,但没有成功。

然后我开始了一个新的 Web Api 项目,为 OData V1-3 添加了所有 NuGet 包,添加了一个 Web Api OData 控制器,并立即成功地进行了任何形式的序列化定制。

这是因为 OData V1-3 与自定义 MediaTypeFormatters 一起使用这一简单事实,就像 WebApi 一样。之后就变得容易了。

我不会插入有关此的代码,因为使用 MediaTypeFormatter 的示例很多,如下所示: http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters

好的,您确实会失去一些 V4 功能: http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#OData

但我相信,您可以摆脱 Microsoft 以一种比您更神圣的方式遵循的严格 V4 Oasis 实施的麻烦。 (可能是因为一开始就是他们的东西)。

V4 中最重要的特性: 支持 OData 模型中的别名属性

  • 谁在乎呢。

在 ODataConventionModelBuilder 中支持 ComplexTypeAttribute、AssociationAttribute、TimesTampAttribute 和 ConcurrencyCheckAttribute

  • 你不需要它们。

提供为操作提供友好标题的能力

  • 谁在乎呢。

与 ODL UriParser 集成

  • 我不明白。

支持枚举、包含和单例

  • 你不需要那个。

支持原始类型的强制转换

  • 你不需要那个。

新增OData功能支持

  • 这很不幸,但可以通过使用 OData 旁边的普通 ApiController 轻松解决。

支持函数调用的参数别名

  • 你不需要那个,反正很乱。

模型中支持驼峰命名约定

  • 很高兴它有支持,但我建议不要这样做。

在 $filter 中支持 cast()

  • 不需要它。

支持开放的复杂类型

  • 是吗?您有自定义序列化。你可以序列化任何你想要的类型。

删除了 EntitySetController 和 AsyncEntitySetController

  • 再见。

将 $link 更改为 $ref

  • 好的。

添加属性路由支持

  • 不幸的是,您被 WebApiConfig 中定义的路由卡住了。大不了。

问问自己,您使用 OData 的目的是什么? 显然,我不能代表您的情况,但是我将对您的用例进行破解: - 您试图允许通过 http 向数据源询问复杂的问题,因为您不希望每次客户端 N 提出新问题时都更改界面。 - 您试图让客户请求他/她返回的数据的实际格式是什么。 CSV? Xml?杰森?电子名片?哎呀,奇怪? pdf?

如果这两个目标是您真正想要实现的两个目标,请坚持使用 V3。实现域模型将为您省去一大堆痛苦。

不过我的 0.02 美元。

【讨论】:

  • 像你一样,我发现这么晚......你如何绕过没有 OData V4 的过滤子集合......它甚至不在 V3 的规范中,而且我的一些查询太大而无法过滤。
猜你喜欢
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-17
  • 2016-08-18
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
相关资源
最近更新 更多