.Net Core 3.1 Web 的自定义 OData 日期时间序列化程序 Api
Custom OData DateTime Serializer for .Net Core 3.1 Web Api
我有一个 OData Api,其中包含一个数据模型对象,其中包含许多可为 null 的 DateTime 字段。
例如
public class Book : EntityBase
{
...
public DateTime? CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
...
}
使用 OData API 的客户端要求将 DateTime 字段格式化为 'yyyy-MM-dd' 格式,而不是像 'yyyy-MM-ddTHH:mm:ss'
这样的默认长格式
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
private readonly CustomODataEntityTypeSerializer _entityTypeSerializer;
public CustomODataSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
_entityTypeSerializer = new CustomODataEntityTypeSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
{
if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
return _entityTypeSerializer;
else
return base.GetEdmTypeSerializer(edmType);
}
}
public class CustomODataEntityTypeSerializer : ODataResourceSerializer
{
public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider) { }
public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
if (property.Name.Contains("Date"))
{
property.Value = ((DateTime)property.Value).ToShortDateString();
}
return property.Value != null ? property : null;
}
}
我也试过“property.Value = ((DateTimeOffset)property.Value).DateTime.ToShortDateString();”而不是上面的。
然后使用
注册此序列化程序
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Expand().OrderBy().Filter().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", a =>
{
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new CustomODataSerializerProvider(sp));
});
//endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
});
但是当调用 OData 端点时出现此错误
can't parse JSON. Raw result:
{"@odata.context":"https://localhost:5000/odata/$metadata#Book","value":[
我还尝试应用 Json 序列化程序,但这对从 OData 端点提供的数据没有影响:
services
.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreNullValues = false;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-dd"));
}
}
我在 .NET Core 3.1 Web Api 中使用 Microsoft.AspNetCore.OData 7.4.1。对于如何更改通过 OData API 提供的数据的 DateTime format/serialization 的任何建议,我们将不胜感激。
声明 EDM 时,您可以使用 Edm.Date
格式将特定字段标记为 de/serialized,例如:
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EnableLowerCamelCase();
builder.EntitySet<Book>("Book");
builder.EntityType<Book>().Property(p => p.CreatedDate).AsDate();
builder.EntityType<Book>().Property(p => p.UpdatedDate).AsDate();
return builder.GetEdmModel();
}
对于如下所示的简单控制器:
namespace WebAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class BookController : ControllerBase
{
private static readonly List<Book> Books = new List<Book>
{
new Book() {
BookId = 1,
CreatedDate = new DateTime(2020, 01, 02),
UpdatedDate = new DateTime(2020, 02, 03)
},
new Book() {
BookId = 2,
CreatedDate = null,
UpdatedDate = null
},
};
[EnableQuery]
public IEnumerable<Book> Get()
{
var result = Books.ToArray();
return result;
}
}
}
您可以看到使用 YYYY-MM-DD 格式序列化的 DateTime?
字段:
{
"@odata.context": "https://localhost:5001/odata/$metadata#Book",
"value": [
{
"createdDate": "2020-01-02",
"updatedDate": "2020-02-03",
"bookId": 1
},
{
"createdDate": null,
"updatedDate": null,
"bookId": 2
}
]
}
并且 https://localhost:5001/odata/$metadata XML 将这些字段声明为 Edm.Date
:
类型
<edmx:Edmx Version="4.0">
<edmx:DataServices>
<Schema Namespace="WebAPI">
<EntityType Name="Book">
<Key>
<PropertyRef Name="bookId"/>
</Key>
<Property Name="createdDate" Type="Edm.Date"/>
<Property Name="updatedDate" Type="Edm.Date"/>
<Property Name="bookId" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
<Schema Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Book" EntityType="WebAPI.Book"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
我有一个 OData Api,其中包含一个数据模型对象,其中包含许多可为 null 的 DateTime 字段。
例如
public class Book : EntityBase
{
...
public DateTime? CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
...
}
使用 OData API 的客户端要求将 DateTime 字段格式化为 'yyyy-MM-dd' 格式,而不是像 'yyyy-MM-ddTHH:mm:ss'
这样的默认长格式public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
private readonly CustomODataEntityTypeSerializer _entityTypeSerializer;
public CustomODataSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
_entityTypeSerializer = new CustomODataEntityTypeSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
{
if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
return _entityTypeSerializer;
else
return base.GetEdmTypeSerializer(edmType);
}
}
public class CustomODataEntityTypeSerializer : ODataResourceSerializer
{
public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider) { }
public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
if (property.Name.Contains("Date"))
{
property.Value = ((DateTime)property.Value).ToShortDateString();
}
return property.Value != null ? property : null;
}
}
我也试过“property.Value = ((DateTimeOffset)property.Value).DateTime.ToShortDateString();”而不是上面的。
然后使用
注册此序列化程序 app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Expand().OrderBy().Filter().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", a =>
{
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new CustomODataSerializerProvider(sp));
});
//endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
});
但是当调用 OData 端点时出现此错误
can't parse JSON. Raw result:
{"@odata.context":"https://localhost:5000/odata/$metadata#Book","value":[
我还尝试应用 Json 序列化程序,但这对从 OData 端点提供的数据没有影响:
services
.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreNullValues = false;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-dd"));
}
}
我在 .NET Core 3.1 Web Api 中使用 Microsoft.AspNetCore.OData 7.4.1。对于如何更改通过 OData API 提供的数据的 DateTime format/serialization 的任何建议,我们将不胜感激。
声明 EDM 时,您可以使用 Edm.Date
格式将特定字段标记为 de/serialized,例如:
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EnableLowerCamelCase();
builder.EntitySet<Book>("Book");
builder.EntityType<Book>().Property(p => p.CreatedDate).AsDate();
builder.EntityType<Book>().Property(p => p.UpdatedDate).AsDate();
return builder.GetEdmModel();
}
对于如下所示的简单控制器:
namespace WebAPI.Controllers
{
[ApiController]
[Route("[controller]")]
public class BookController : ControllerBase
{
private static readonly List<Book> Books = new List<Book>
{
new Book() {
BookId = 1,
CreatedDate = new DateTime(2020, 01, 02),
UpdatedDate = new DateTime(2020, 02, 03)
},
new Book() {
BookId = 2,
CreatedDate = null,
UpdatedDate = null
},
};
[EnableQuery]
public IEnumerable<Book> Get()
{
var result = Books.ToArray();
return result;
}
}
}
您可以看到使用 YYYY-MM-DD 格式序列化的 DateTime?
字段:
{
"@odata.context": "https://localhost:5001/odata/$metadata#Book",
"value": [
{
"createdDate": "2020-01-02",
"updatedDate": "2020-02-03",
"bookId": 1
},
{
"createdDate": null,
"updatedDate": null,
"bookId": 2
}
]
}
并且 https://localhost:5001/odata/$metadata XML 将这些字段声明为 Edm.Date
:
<edmx:Edmx Version="4.0">
<edmx:DataServices>
<Schema Namespace="WebAPI">
<EntityType Name="Book">
<Key>
<PropertyRef Name="bookId"/>
</Key>
<Property Name="createdDate" Type="Edm.Date"/>
<Property Name="updatedDate" Type="Edm.Date"/>
<Property Name="bookId" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
<Schema Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Book" EntityType="WebAPI.Book"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>