【问题标题】:Custom OData DateTime Serializer for .Net Core 3.1 Web Api.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序
【发布时间】:2021-02-10 01:30:22
【问题描述】:

我有一个带有数据模型对象的 OData Api,该对象带有许多可为空的 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 格式/序列化的建议将不胜感激。

【问题讨论】:

    标签: datetime serialization odata asp.net-core-webapi jsonserializer


    【解决方案1】:

    在声明您的 EDM 时,您可以使用 Edm.Date 格式标记要反序列化的特定字段,例如:

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-21
      • 1970-01-01
      • 2016-08-18
      • 1970-01-01
      • 2019-04-08
      • 2010-09-23
      • 1970-01-01
      相关资源
      最近更新 更多