如何使用ASP.Net core ModelMetadata属性

问题描述 投票:0回答:3
[Table("LegalEntity")]
[ModelMetadataType(typeof(LegalEntityMeta))]
public class LegalEntity : Entity<long>
{
}

public class LegalEntityMeta
{
    [JsonProperty(PropertyName = "LegalEntityId")]
    public long Id { get; set; }

    [JsonProperty(PropertyName = "LegalEntityName")]
    public string Name { get; set; }
}

在 Startup.cs 中....

        services
            .AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            })
            .AddAutoMapper(typeof(Startup))
            .AddMvcCore()
            .AddJsonFormatters()
            .AddApiExplorer();

我的期望是看到带有属性 legalEntityId 和 legalEntityName 的 json,但生成的 json 具有 id 和 name 作为属性。 有人可以帮助我如何更改 json 属性吗? 谢谢 阿南德

c# json.net asp.net-core-2.0 data-annotations
3个回答
6
投票

Json.NET 目前不支持

Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute
。在 问题 #1349:为 dotnetcore 添加对 ModelMetadataType 的支持,就像以前版本中支持的 MetadataTypeAttribute 实现对其支持的请求已被拒绝。

Json.NET确实支持

System.ComponentModel.DataAnnotations.MetadataTypeAttribute
,尽管存在这个答案中描述的一些限制,但是即使这个属性存在于.Net core中(不确定)它也不会帮助你,因为你正在尝试使用派生类的元数据类型来重命名“基类型”中的属性,这不是元数据类型信息的预期用途。 IE。以下是开箱即用的(完整的.Net): [System.ComponentModel.DataAnnotations.MetadataType(typeof(EntityMeta))] public class Entity<T> { public T Id { get; set; } public string Name { get; set; } } public class EntityMeta { [JsonProperty(PropertyName = "LegalEntityId")] public long Id { get; set; } [JsonProperty(PropertyName = "LegalEntityName")] public string Name { get; set; } }

但以下情况则不然:

[System.ComponentModel.DataAnnotations.MetadataType(typeof(LegalEntityMeta))] public class LegalEntity : Entity<long> { } public class LegalEntityMeta { [JsonProperty(PropertyName = "LegalEntityId")] public long Id { get; set; } [JsonProperty(PropertyName = "LegalEntityName")] public string Name { get; set; } }

为什么 Json.NET 不允许派生类型元数据信息修改基本类型契约?您必须询问 Newtonsoft,但猜测包括:

    Json.NET 是一个基于契约的序列化器,其中每种类型通过属性指定其契约。并不是说一种类型可以重写第二种类型的合约。
  1. DataContractJsonSerializer

    DataContractSerializer
    的工作方式相同。
    
    

  2. 这样做会违反
  3. 里氏替换原则

  4. 那么,你有什么选择?

    您可以序列化一个
  1. DTO

    来代替您的 LegalEntity,并使用类似

    automapper
    的东西来映射:public class LegalEntityDTO { [JsonProperty(PropertyName = "LegalEntityId")] public long Id { get; set; } [JsonProperty(PropertyName = "LegalEntityName")] public string Name { get; set; } }

  2. 您可以使用必要的逻辑为
  3. JsonConverter

    创建一个 自定义

    LegalEntity
    
    

  4. 您可以使用必要的逻辑创建一个
  5. 自定义合约解析器

    ,类似于此处,例如以下内容: using System.Reflection; public class ModelMetadataTypeAttributeContractResolver : DefaultContractResolver { public ModelMetadataTypeAttributeContractResolver() { // Default from https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Formatters.Json/JsonSerializerSettingsProvider.cs this.NamingStrategy = new CamelCaseNamingStrategy(); } const string ModelMetadataTypeAttributeName = "Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute"; const string ModelMetadataTypeAttributeProperty = "MetadataType"; protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); var propertyOverrides = GetModelMetadataTypes(type) .SelectMany(t => t.GetProperties()) .ToLookup(p => p.Name, p => p); foreach (var property in properties) { var metaProperty = propertyOverrides[property.UnderlyingName].FirstOrDefault(); if (metaProperty != null) { var jsonPropertyAttribute = metaProperty.GetCustomAttributes<JsonPropertyAttribute>().FirstOrDefault(); if (jsonPropertyAttribute != null) { property.PropertyName = jsonPropertyAttribute.PropertyName; // Copy other attributes over if desired. } } } return properties; } static Type GetModelMetadataType(Attribute attribute) { var type = attribute.GetType(); if (type.FullName == ModelMetadataTypeAttributeName) { var property = type.GetProperty(ModelMetadataTypeAttributeProperty); if (property != null && property.CanRead) { return property.GetValue(attribute, null) as Type; } } return null; } static Type[] GetModelMetadataTypes(Type type) { var query = from t in type.BaseTypesAndSelf() from a in t.GetCustomAttributes(false).Cast<System.Attribute>() let metaType = GetModelMetadataType(a) where metaType != null select metaType; return query.ToArray(); } } public static partial class TypeExtensions { public static IEnumerable<Type> BaseTypesAndSelf(this Type type) { while (type != null) { yield return type; type = type.BaseType; } } }

    示例
    .Net fiddle

    . 要直接序列化,请执行以下操作:

    var settings = new JsonSerializerSettings { ContractResolver = new ModelMetadataTypeAttributeContractResolver(), }; var json = JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

    要将合同解析器安装到 Asp.Net Core 中,请参阅
    此处

    注意我使用完整的 .Net 4.5.1 编写了此内容,因此它只是一个原型。 .Net Core 使用

    不同的反射 API

    ,但是如果您按照此处所述安装System.Reflection.TypeExtensions,我相信它应该可以工作。


0
投票

    添加nuget包Microsoft.AspNetCore.Mvc.Newtonsoft.Json
  1. 在 Startup.cs -> 配置服务中(阅读
  2. https://www.ryadel.com/en/use-json-net-instead-of-system-text-json-in-asp-net-core-3- mvc-projects/

    了解更多信息) services.AddControllers().AddNewtonsoftJson();

  3. 替换为 System.Text.Json.Serialization 并使用 MetadataType 而不是 ModelMetadataType:
  4. using Newtonsoft.Json; namespace YourDbDataNamespace { [MetadataType(typeof(UserMetadata))] public partial class User {} public class UserMetadata { [JsonProperty(PropertyName = "LegalEntityId")] int Id { get; set; } [JsonIgnore] public string PasswordHash { get; set; } } }

    
        

0
投票

某些类首先使用数据库生成:

public class Entity<T> { public T Id { get; set; } public string Name { get; set; } }

创建MetadataBasedConverter.cs

using System.Reflection; using System.Text.Json; namespace Mynamespace.JsonConverter; public class MetadataBasedConverter : JsonConverter<object> { private PropertyInfo[] GetCombinedProperties(Type type) { var metadataAttr = type.GetCustomAttribute<MetadataTypeAttribute>(); var metadataProps = metadataAttr?.MetadataClassType.GetProperties() ?? Array.Empty<PropertyInfo>(); var originalProps = type.GetProperties(); return originalProps.Union(metadataProps).ToArray(); } public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { object result = (object)Activator.CreateInstance(typeToConvert)!; var combinedProps = GetCombinedProperties(typeToConvert); foreach (JsonProperty prop in doc.RootElement.EnumerateObject()) { foreach (var property in combinedProps) { var metaAttr = property.GetCustomAttribute<JsonPropertyNameAttribute>(); var nameToUse = metaAttr?.Name ?? property.Name; if (prop.Name.Equals(nameToUse, StringComparison.OrdinalIgnoreCase)) { var actualProperty = typeToConvert.GetProperty(property.Name); if (actualProperty != null) { object? value = JsonSerializer.Deserialize(prop.Value.GetRawText(), actualProperty.PropertyType); actualProperty.SetValue(result, value); break; } } } } return result; } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { writer.WriteStartObject(); var combinedProps = GetCombinedProperties(value.GetType()); foreach (var property in combinedProps) { var metaAttr = property.GetCustomAttribute<JsonPropertyNameAttribute>(); var nameToUse = metaAttr?.Name ?? property.Name; var actualProperty = value.GetType().GetProperty(property.Name); if (actualProperty != null) { writer.WritePropertyName(nameToUse); JsonSerializer.Serialize(writer, actualProperty.GetValue(value), actualProperty.PropertyType, options); } } writer.WriteEndObject(); } public override bool CanConvert(Type typeToConvert) { return typeof(object).IsAssignableFrom(typeToConvert); } }

创建一个分部类,并添加 MetadataTypeAttribute 和 MetadataBasedConverter

[MetadataType(typeof(Metadata))] [JsonConverter(typeof(MetadataBasedConverter))] public partial class Entity<T> { internal sealed class Metadata { [JsonPropertyName("LegalEntityId")] public long Id { get; set; } [JsonPropertyName("LegalEntityName")] public string Name { get; set; } } }

© www.soinside.com 2019 - 2024. All rights reserved.