.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>