OData序列化

时间:2014-01-28 11:26:10

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

我一直在努力寻找一种解决方案,用于自定义序列化过去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. 对客户的任何GET请求(以及当然客户 - 即IQueryable<>)将通过此序列化程序,该字符串将使用“CompanyName”替换字段“VirtualField1”的名称如果是每个客户的收藏品。
  2. 任何POST请求都将通过相反的替换 - 即 - 将“CompanyName”替换为“VirtualField1”。
    **实际的替换逻辑有点复杂,但想法是一样的。
  3. 我已经阅读了谷歌本可以找到的所有内容,但找不到任何有效的例子。 以下是一些链接:
    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功能 - 即 - 例如:$ exapnd,如/ odata / Customers?$ expand =图像完全消失,根本不起作用(这是响应):< / p>

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

    虽然没有添加自定义格式化程序,但此实例中的图片已展开。

    任何想法,想法,方向???

1 个答案:

答案 0 :(得分:5)

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

我认为你正在使用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

但是我相信你可以在没有严格的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? JSON?电子名片?哎呀,奥特? PDF?

如果这两个目标是你真正希望实现的目标,请坚持使用V3。实现领域模型,你将为自己省去一大堆痛苦。

我的0.02美元虽然。