json.net – 如何使用ASP.Net核心ModelMetadata属性

[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属性?
谢谢
阿南德

Json.NET目前不支持 Microsoft.AspNetCore.Mvc.ModelMetadataTypeAttribute.在 Issue #1349: Add support for ModelMetadataType for dotnetcore like supported MetadataTypeAttribute in previous versions,实施支持的请求被拒绝.

Json.NET确实支持System.ComponentModel.DataAnnotations.MetadataTypeAttribute,尽管有一些在this answer中描述的限制,但是即使这个属性出现在.Net核心中(不确定它)它也无济于事,因为你试图使用派生的元数据类型用于重命名基类型属性的类,这不是元数据类型信息的预期用途.即以下工作开箱即用(完整.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不允许派生类型元数据信息来修改基类型契约?你不得不问问牛顿软件,但猜测包括:

> Json.NET是一个基于合同的序列化程序,其中每种类型都通过属性指定其合同.并不是说一种类型可以重写第二种类型的合同.
> DataContractJsonSerializer和DataContractSerializer的工作方式相同.
>这样做会违反Liskov substitution principle.

那么,你有什么选择?

>您可以序列化DTO代替您的LegalEntity,并使用类似automapper之类的内容进行映射:

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

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

>您可以使用必要的逻辑为LegalEntity创建custom JsonConverter.
>您可以使用必要的逻辑创建custom contract resolver,类似于here,例如:

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,请参阅here.

注意我使用完整的.Net 4.5.1编写了这个,所以它只是一个原型. .Net Core使用different reflection API,但如果您按照here所述安装System.Reflection.TypeExtensions,我相信它应该可行.

相关文章

文章浏览阅读2.4k次。最近要优化cesium里的热力图效果,浏览...
文章浏览阅读1.2w次,点赞3次,收藏19次。在 Python中读取 j...
文章浏览阅读1.4k次。首字母缩略词 API 代表应用程序编程接口...
文章浏览阅读802次,点赞10次,收藏10次。解决一个JSON反序列...
文章浏览阅读882次。Unity Json和Xml的序列化和反序列化_uni...