OData V4 无法正确序列化包含 System.Object 属性 的 POCO(?) 列表
OData V4 failing to properly serialize list of POCO(?)s containing a System.Object property
我正在使用 Web API 2 使用当前可用的 OData NuGet 包编写 OData V4 服务。
我有一个 class Foo
的实体集,如下所示:
class Foo {
string SomePropertyUnrelatedToThePost {get; set;}
...
IList<Bar> TheImportantPropertyList {get; set;}
}
Bar
没有太多进展:
class Bar {
string Name {get; set;}
int? Group {get; set;}
object Value {get; set;}
}
在使用中,Bar#Value 除了基本值外,从未被分配任何其他值,但有些是原始值,有些不是:bool、byte、char、short、int、long、string、Decimal、DateTime...
我正在按照文档说明注册 Foo 集,使用 ODataConventionModelBuilder,如下所示:
...
builder.EntitySet<Foo>("Foos");
并将我的 Bar
注册为具有 builder.ComplexType<Bar>();
的复杂类型似乎并没有改变这里的结果。
问题是当我在我的 ODataController 中 return 一个 Foo 对象时,JSON 响应不包括 Bar#Value。
{
...
"SomePropertyUnrelatedToThePost": "Foo was here",
...
"TheImportantPropertyList": [
{
"Name": "TheAnswer",
"Group": null
},
{
"Name": "TheQuestion",
"Group": null
}
]
}
让我更加困惑的是,我可以在我的控制器方法中手动序列化一个 Foo,如下所示:
var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;//.CreateJsonSerializer();
var s = JsonSerializer.Create(settings);
...
var json = Encoding.Default.GetString(...);
生成正确序列化的结果:
{
"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
}
]
}
我是否错误地配置了 OData?我还有其他一些核心误解吗?
当我写这个问题时,我突然想到,如果我更改我的模型以包含分配值 属性 的 System.Type,我可以编写一个自定义序列化程序,但它似乎不必来吧。
编辑:当我手动序列化我的 Foo
时,我没有使用默认的 OData 序列化程序,我使用的是新的 Newtonsoft JsonSerializer。默认的 OData 序列化器和反序列化器根本不喜欢 Object
类型的属性。
我搞定了。 This post 有帮助。作为 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:
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);
}
}
在我的自定义序列化程序中,我注意到 OData 不会自动将 DateTime
转换为 DateTimeOffset
。我的序列化器:
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;
}
}
我知道这是一个非常具体的问题和答案,但我希望有人觉得它有用。
我正在使用 Web API 2 使用当前可用的 OData NuGet 包编写 OData V4 服务。
我有一个 class Foo
的实体集,如下所示:
class Foo {
string SomePropertyUnrelatedToThePost {get; set;}
...
IList<Bar> TheImportantPropertyList {get; set;}
}
Bar
没有太多进展:
class Bar {
string Name {get; set;}
int? Group {get; set;}
object Value {get; set;}
}
在使用中,Bar#Value 除了基本值外,从未被分配任何其他值,但有些是原始值,有些不是:bool、byte、char、short、int、long、string、Decimal、DateTime...
我正在按照文档说明注册 Foo 集,使用 ODataConventionModelBuilder,如下所示:
...
builder.EntitySet<Foo>("Foos");
并将我的 Bar
注册为具有 builder.ComplexType<Bar>();
的复杂类型似乎并没有改变这里的结果。
问题是当我在我的 ODataController 中 return 一个 Foo 对象时,JSON 响应不包括 Bar#Value。
{
...
"SomePropertyUnrelatedToThePost": "Foo was here",
...
"TheImportantPropertyList": [
{
"Name": "TheAnswer",
"Group": null
},
{
"Name": "TheQuestion",
"Group": null
}
]
}
让我更加困惑的是,我可以在我的控制器方法中手动序列化一个 Foo,如下所示:
var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;//.CreateJsonSerializer();
var s = JsonSerializer.Create(settings);
...
var json = Encoding.Default.GetString(...);
生成正确序列化的结果:
{
"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
}
]
}
我是否错误地配置了 OData?我还有其他一些核心误解吗? 当我写这个问题时,我突然想到,如果我更改我的模型以包含分配值 属性 的 System.Type,我可以编写一个自定义序列化程序,但它似乎不必来吧。
编辑:当我手动序列化我的 Foo
时,我没有使用默认的 OData 序列化程序,我使用的是新的 Newtonsoft JsonSerializer。默认的 OData 序列化器和反序列化器根本不喜欢 Object
类型的属性。
我搞定了。 This post 有帮助。作为 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:
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);
}
}
在我的自定义序列化程序中,我注意到 OData 不会自动将 DateTime
转换为 DateTimeOffset
。我的序列化器:
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;
}
}
我知道这是一个非常具体的问题和答案,但我希望有人觉得它有用。