简体   繁体   English

OData V4无法正确序列化包含System.Object属性的POCO(?)列表

[英]OData V4 failing to properly serialize list of POCO(?)s containing a System.Object property

I'm writing an OData V4 service with Web API 2 using the currently available OData NuGet packages. 我正在使用当前可用的OData NuGet包编写带有Web API 2的OData V4服务。

I have an Entity Set of class Foo like so: 我有一个类Foo的实体集,如下所示:

class Foo {
    string SomePropertyUnrelatedToThePost {get; set;}
    ...
    IList<Bar> TheImportantPropertyList {get; set;}
}

Bar doesn't have too much going on: Bar没有太多进展:

class Bar {
    string Name {get; set;}
    int? Group {get; set;}
    object Value {get; set;}
}

In use, Bar#Value is never assigned anything other than basic values, but some are primitives and some are not: bool, byte, char, short, int, long, string, Decimal, DateTime... 在使用中,Bar#Value永远不会被赋予基本值以外的任何东西,但有些是基元,有些则不是:bool,byte,char,short,int,long,string,Decimal,DateTime ...

I am registering the Foo set as the docs instruct, using an ODataConventionModelBuilder like so: 我正在使用ODataConventionModelBuilder注册Foo集作为文档指令,如下所示:

...
builder.EntitySet<Foo>("Foos"); 

and registering my Bar as a complex type with builder.ComplexType<Bar>(); 并使用builder.ComplexType<Bar>();将我的Bar注册为复杂类型builder.ComplexType<Bar>(); does not seem to change the outcome here. 似乎没有改变这里的结果。

The problem is that when I return a Foo object in my ODataController, the JSON response does not include Bar#Value. 问题是当我在ODataController中返回一个Foo对象时,JSON响应不包含Bar#Value。

{
  ...
  "SomePropertyUnrelatedToThePost": "Foo was here",
  ...
  "TheImportantPropertyList": [
     {
        "Name": "TheAnswer",
        "Group": null
     },
     {
        "Name": "TheQuestion",
        "Group": null
     }
   ]
}

Adding to my confusion is the fact that I can manually serialize a Foo in my controller method like so: 令我困惑的是,我可以在我的控制器方法中手动序列化Foo,如下所示:

var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;//.CreateJsonSerializer();
var s = JsonSerializer.Create(settings);
...
var json = Encoding.Default.GetString(...);

to produce a properly serialized result: 生成一个正确的序列化结果:

{
  "SomePropertyUnrelatedToThePost": "Foo was here",
  ...
  "TheImportantPropertyList": [
     {
        "Name": "TheAnswer",
        "Value": 42,
        "Group": null
     },
     {
        "Name": "TheQuestion",
        "Value": "What is the airspeed velocity of an unladen swallow?",
        "Group": null
     }
   ]
}

Am I configuring OData incorrectly? 我是否错误地配置了OData? Do I have some other core misunderstanding? 我还有其他一些核心误区吗? As I wrote this question it occurred to me that if I changed my model to include the System.Type of the assigned Value property, I could write a custom serializer, but it seems like it shouldn't have to come to that. 当我写这个问题时,我想到如果我将模型更改为包含指定Value属性的System.Type,我可以编写自定义序列化程序,但似乎不应该这样做。

Edit : When I'm manually serializing my Foo , I'm not using the default OData serializer, I'm using a new Newtonsoft JsonSerializer. 编辑 :当我手动序列化我的Foo ,我没有使用默认的OData序列化程序,我正在使用新的Newtonsoft JsonSerializer。 The default OData serializer and deserializers simply do not like properties of type Object . 默认的OData序列化程序和反序列化程序根本不喜欢Object类型的属性。

I got this going. 我明白了。 This post helped. 这篇文章有帮助。 Being new to OData, it took a while to get through the documentation, as most of it is out of date. 作为OData的新手,需要一段时间才能完成文档,因为大部分内容都已过时。

In my WebApiConfig.cs , I used the new method of injecting an ODataSerializerProvider into OData: 在我的WebApiConfig.cs ,我使用了将ODataSerializerProvider注入OData的新方法:

config.MapODataServiceRoute("odata", "api/v1", b =>
               b.AddService(ServiceLifetime.Singleton, sp => builder.GetEdmModel())
                .AddService<ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new MySerializerProvider(sp)));

MySerializerProvider: MySerializerProvider:

internal sealed class MySerializerProvider : DefaultODataSerializerProvider
{
    private MySerializer _mySerializer;

    public MySerializerProvider(IServiceProvider sp) : base(sp)
    {
        _mySerializer = new MySerializer(this);
    }

    public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
    {
        var fullName = edmType.FullName();

        if (fullName == "Namespace.Bar")
            return _mySerializer;            
        else
            return base.GetEdmTypeSerializer(edmType);            
    }        
}

In my custom serializer, I noted OData will not automatically convert a DateTime to a DateTimeOffset . 在我的自定义序列化程序中,我注意到OData不会自动将DateTime转换为DateTimeOffset MySerializer: MySerializer:

internal sealed class MySerializer : ODataResourceSerializer
{
  public MySerializer(ODataSerializerProvider sp) : base(sp) { }

  public override ODataResource CreateResource(SelectExpandNode selectExpandNode, ResourceContext resourceContext)
  {
      ODataResource resource = base.CreateResource(selectExpandNode, resourceContext);

      if (resource != null && resourceContext.ResourceInstance is Bar b)
          resource = BarToResource(b);

      return resource;
  }

  private ODataResource BarToResource(Bar b)
  {
      var odr = new ODataResource
      {
          Properties = new List<ODataProperty>
          {
              new ODataProperty
              {
                  Name = "Name",
                  Value = b.Name
              },
              new ODataProperty
              {
                  Name = "Value",
                  Value = b.Value is DateTime dt ? new DateTimeOffset(dt) : b.Value
              },
              new ODataProperty
              {
                  Name = "Group",
                  Value = b.Group
              },
          }
      };

      return odr;
  }
}

I realize this is a pretty specific question and answer but I hope someone finds it useful. 我意识到这是一个非常具体的问题和答案,但我希望有人发现它有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM