本文介绍了Newtonsoft JSON动态属性名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有一种方法可以在序列化期间更改Data属性的名称,因此可以在WEB Api中重用该类.

Is there a way to change name of Data property during serialization, so I can reuse this class in my WEB Api.

例如,如果我要返回用户的分页列表,则应将Data属性序列化为用户",如果要返回项目列表,则应将其称为项目",等等.

For an example, if i am returning paged list of users, Data property should be serialized as "users", if i'm returning list of items, should be called "items", etc.

是否可能这样:

public class PagedData
{
    [JsonProperty(PropertyName = "Set from constructor")]??
    public IEnumerable<T> Data { get; private set; }
    public int Count { get; private set; }
    public int CurrentPage { get; private set; }
    public int Offset { get; private set; }
    public int RowsPerPage { get; private set; }
    public int? PreviousPage { get; private set; }
    public int? NextPage { get; private set; }
}

我希望对此功能进行控制,例如,尽可能传递名称.如果我的class称为UserDTO,我仍然希望序列化的属性称为Users,而不是UserDTOs.

I would like to have a control over this functionality, such as passing name to be used if possible. If my class is called UserDTO, I still want serialized property to be called Users, not UserDTOs.

示例

var usersPagedData = new PagedData("Users", params...);

推荐答案

您可以使用自定义ContractResolver进行此操作.解析器可以寻找一个自定义属性,该属性将表明您希望JSON属性的名称基于可枚举项目的类.如果项目类具有指定其复数名称的另一个属性,则该名称将用于可枚举属性,否则,项目类名称本身将被复数并用作可枚举属性名称.下面是您需要的代码.

You can do this with a custom ContractResolver. The resolver can look for a custom attribute which will signal that you want the name of the JSON property to be based on the class of the items in the enumerable. If the item class has another attribute on it specifying its plural name, that name will then be used for the enumerable property, otherwise the item class name itself will be pluralized and used as the enumerable property name. Below is the code you would need.

首先让我们定义一些自定义属性:

First let's define some custom attributes:

public class JsonPropertyNameBasedOnItemClassAttribute : Attribute
{
}

public class JsonPluralNameAttribute : Attribute
{
    public string PluralName { get; set; }
    public JsonPluralNameAttribute(string pluralName)
    {
        PluralName = pluralName;
    }
}

然后是解析器:

public class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        if (prop.PropertyType.IsGenericType && member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
        {
            Type itemType = prop.PropertyType.GetGenericArguments().First();
            JsonPluralNameAttribute att = itemType.GetCustomAttribute<JsonPluralNameAttribute>();
            prop.PropertyName = att != null ? att.PluralName : Pluralize(itemType.Name);
        }
        return prop;
    }

    protected string Pluralize(string name)
    {
        if (name.EndsWith("y") && !name.EndsWith("ay") && !name.EndsWith("ey") && !name.EndsWith("oy") && !name.EndsWith("uy"))
            return name.Substring(0, name.Length - 1) + "ies";

        if (name.EndsWith("s"))
            return name + "es";

        return name + "s";
    }
}

现在,您可以使用[JsonPropertyNameBasedOnItemClass]属性装饰PagedData<T>类中的可变名称属性:

Now you can decorate the variably-named property in your PagedData<T> class with the [JsonPropertyNameBasedOnItemClass] attribute:

public class PagedData<T>
{
    [JsonPropertyNameBasedOnItemClass]
    public IEnumerable<T> Data { get; private set; }
    ...
}

并使用[JsonPluralName]属性装饰DTO类:

And decorate your DTO classes with the [JsonPluralName] attribute:

[JsonPluralName("Users")]
public class UserDTO
{
    ...
}

[JsonPluralName("Items")]
public class ItemDTO
{
    ...
}

最后,要进行序列化,请创建JsonSerializerSettings的实例,设置ContractResolver属性,然后将设置传递给JsonConvert.SerializeObject,如下所示:

Finally, to serialize, create an instance of JsonSerializerSettings, set the ContractResolver property, and pass the settings to JsonConvert.SerializeObject like so:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    ContractResolver = new CustomResolver()
};

string json = JsonConvert.SerializeObject(pagedData, settings);

提琴: https://dotnetfiddle.net/GqKBnx

如果您正在使用Web API(看起来像您一样),则可以通过WebApiConfig类(在App_Start文件夹中)的Register方法将自定义解析器安装到管道中.

If you're using Web API (looks like you are), then you can install the custom resolver into the pipeline via the Register method of the WebApiConfig class (in the App_Start folder).

JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CustomResolver();


另一种方法

另一种可能的方法是使用自定义JsonConverter专门处理PagedData类的序列化,而不是使用上面介绍的更通用的解析器+属性"方法.转换器方法要求PagedData类上有另一个属性,该属性指定要用于可枚举的Data属性的JSON名称.您可以在PagedData构造函数中传递此名称,也可以单独设置它,只要您在序列化时间之前执行即可.转换器将为该名称查找该名称,并在为可枚举属性写出JSON时使用它.


Another Approach

Another possible approach uses a custom JsonConverter to handle the serialization of the PagedData class specifically instead using the more general "resolver + attributes" approach presented above. The converter approach requires that there be another property on your PagedData class which specifies the JSON name to use for the enumerable Data property. You could either pass this name in the PagedData constructor or set it separately, as long as you do it before serialization time. The converter will look for that name and use it when writing out JSON for the enumerable property.

这是转换器的代码:

public class PagedDataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(PagedData<>);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType();

        var bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
        string dataPropertyName = (string)type.GetProperty("DataPropertyName", bindingFlags).GetValue(value);
        if (string.IsNullOrEmpty(dataPropertyName))
        {
            dataPropertyName = "Data";
        }

        JObject jo = new JObject();
        jo.Add(dataPropertyName, JArray.FromObject(type.GetProperty("Data").GetValue(value)));
        foreach (PropertyInfo prop in type.GetProperties().Where(p => !p.Name.StartsWith("Data")))
        {
            jo.Add(prop.Name, new JValue(prop.GetValue(value)));
        }
        jo.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

要使用此转换器,请首先在您的PagedData类中添加一个名为DataPropertyName的字符串属性(如果您愿意,它可以是私有的),然后在该类中添加一个[JsonConverter]属性以将其绑定到转换器:

To use this converter, first add a string property called DataPropertyName to your PagedData class (it can be private if you like), then add a [JsonConverter] attribute to the class to tie it to the converter:

[JsonConverter(typeof(PagedDataConverter))]
public class PagedData<T>
{
    private string DataPropertyName { get; set; }
    public IEnumerable<T> Data { get; private set; }
    ...
}

就是这样.只要设置了DataPropertyName属性,转换器将在序列化时将其拾取.

And that's it. As long as you've set the DataPropertyName property, it will be picked up by the converter on serialization.

提琴: https://dotnetfiddle.net/8E8fEE

这篇关于Newtonsoft JSON动态属性名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 10:37