有没有办法在序列化期间更改Data属性的名称,所以我可以在WEB Api中重用这个类。
例如,如果我要返回分页用户列表,则应将数据属性序列化为"用户",如果我要返回项目列表,则应调用" items& #34;等等。
这样的事情是可能的:
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
。
实施例
var usersPagedData = new PagedData("Users", params...);
答案 0 :(得分:16)
您可以使用自定义ContractResolver
执行此操作。解析器可以查找自定义属性,该属性将表示您希望JSON属性的名称基于枚举中的项的类。如果item类具有指定其复数名称的另一个属性,则该名称将用于可枚举属性,否则项类名称本身将被复数并用作可枚举属性名称。以下是您需要的代码。
首先让我们定义一些自定义属性:
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";
}
}
现在,您可以使用PagedData<T>
属性修饰[JsonPropertyNameBasedOnItemClass]
类中具有可变名称的属性:
public class PagedData<T>
{
[JsonPropertyNameBasedOnItemClass]
public IEnumerable<T> Data { get; private set; }
...
}
使用[JsonPluralName]
属性装饰您的DTO课程:
[JsonPluralName("Users")]
public class UserDTO
{
...
}
[JsonPluralName("Items")]
public class ItemDTO
{
...
}
最后,要序列化,创建JsonSerializerSettings
的实例,设置ContractResolver
属性,并将设置传递给JsonConvert.SerializeObject
,如下所示:
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CustomResolver()
};
string json = JsonConvert.SerializeObject(pagedData, settings);
小提琴:https://dotnetfiddle.net/GqKBnx
如果你正在使用Web API(看起来像你),那么你可以通过Register
类的WebApiConfig
方法将自定义解析器安装到管道中(在{{1}中)文件夹)。
App_Start
另一种可能的方法是使用自定义JsonSerializerSettings settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CustomResolver();
来处理JsonConverter
类的序列化,而不是使用上面提到的更通用的“解析器+属性”方法。转换器方法要求PagedData
类上有另一个属性,它指定用于可枚举PagedData
属性的JSON名称。您可以在Data
构造函数中传递此名称,也可以单独设置,只要在序列化时间之前执行此操作即可。转换器将查找该名称,并在为可枚举属性写出JSON时使用它。
以下是转换器的代码:
PagedData
要使用此转换器,首先将一个名为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();
}
}
的字符串属性添加到DataPropertyName
类(如果您愿意,可以将其设为私有),然后将PagedData
属性添加到该类中把它绑在转换器上:
[JsonConverter]
就是这样。只要您设置了[JsonConverter(typeof(PagedDataConverter))]
public class PagedData<T>
{
private string DataPropertyName { get; set; }
public IEnumerable<T> Data { get; private set; }
...
}
属性,转换器就会在序列化时将其选中。
答案 1 :(得分:6)
另一个选项,不需要使用json格式化程序或使用字符串替换 - 只有继承和覆盖(仍然不是很好的解决方案,imo):
public class MyUser { }
public class MyItem { }
// you cannot use it out of the box, because it's abstract,
// i.e. only for what's intended [=implemented].
public abstract class PaginatedData<T>
{
// abstract, so you don't forget to override it in ancestors
public abstract IEnumerable<T> Data { get; }
public int Count { get; }
public int CurrentPage { get; }
public int Offset { get; }
public int RowsPerPage { get; }
public int? PreviousPage { get; }
public int? NextPage { get; }
}
// you specify class explicitly
// name is clear,.. still not clearer than PaginatedData<MyUser> though
public sealed class PaginatedUsers : PaginatedData<MyUser>
{
// explicit mapping - more agile than implicit name convension
[JsonProperty("Users")]
public override IEnumerable<MyUser> Data { get; }
}
public sealed class PaginatedItems : PaginatedData<MyItem>
{
[JsonProperty("Items")]
public override IEnumerable<MyItem> Data { get; }
}
答案 2 :(得分:2)
这是一个解决方案,不需要对使用Json序列化程序的方式进行任何更改。实际上,它也应该与其他序列化器一起使用。它使用酷DynamicObject类。
用法就像你想要的那样:
var usersPagedData = new PagedData<User>("Users");
....
public class PagedData<T> : DynamicObject
{
private string _name;
public PagedData(string name)
{
if (name == null)
throw new ArgumentNullException(nameof(name));
_name = name;
}
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; }
public override IEnumerable<string> GetDynamicMemberNames()
{
yield return _name;
foreach (var prop in GetType().GetProperties().Where(p => p.CanRead && p.GetIndexParameters().Length == 0 && p.Name != nameof(Data)))
{
yield return prop.Name;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name == _name)
{
result = Data;
return true;
}
return base.TryGetMember(binder, out result);
}
}
答案 3 :(得分:1)
我开发了一个名为 SerializationInterceptor 的包。这是 GitHub 链接:https://github.com/Dorin-Mocan/SerializationInterceptor/wiki。您还可以使用 Nuget 包管理器安装该包。
下面的示例使用 Newtonsoft.Json 进行序列化。您可以使用任何其他工具,因为此包不依赖任何工具。
你可以创建一个拦截器
public class JsonPropertyInterceptorAttribute : SerializationInterceptor.Attributes.InterceptorAttribute
{
public JsonPropertyInterceptorAttribute(string interceptorId)
: base(interceptorId, typeof(Newtonsoft.Json.JsonPropertyAttribute))
{
}
protected override SerializationInterceptor.Attributes.AttributeBuilderParams Intercept(SerializationInterceptor.Attributes.AttributeBuilderParams originalAttributeBuilderParams, object context)
{
string theNameYouWant;
switch (InterceptorId)
{
case "some id":
theNameYouWant = (string)context;
break;
default:
return originalAttributeBuilderParams;
}
originalAttributeBuilderParams.ConstructorArgs = new[] { theNameYouWant };
return originalAttributeBuilderParams;
}
}
并将拦截器放在 Data 道具上
public class PagedData<T>
{
[JsonPropertyInterceptor("some id")]
[Newtonsoft.Json.JsonProperty("during serialization this value will be replaced with the one passed in context")]
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; }
}
然后你可以像这样序列化对象
var serializedObj = SerializationInterceptor.Interceptor.InterceptSerialization(
obj,
objType,
(o, t) =>
{
var serializer = new Newtonsoft.Json.JsonSerializer();
using var stream = new MemoryStream();
using var streamWriter = new StreamWriter(stream);
using var jsonTextWriter = new Newtonsoft.Json.JsonTextWriter(streamWriter);
serializer.Serialize(jsonTextWriter, o, t);
jsonTextWriter.Flush();
return Encoding.Default.GetString(stream.ToArray());
},
context: "the name you want");
希望这对你有用。
答案 4 :(得分:0)
答案 5 :(得分:0)
以下是在.NET Standard 2中测试的另一种解决方案。
public class PagedResult<T> where T : class
{
[JsonPropertyNameBasedOnItemClassAttribute]
public List<T> Results { get; set; }
[JsonProperty("count")]
public long Count { get; set; }
[JsonProperty("total_count")]
public long TotalCount { get; set; }
[JsonProperty("current_page")]
public long CurrentPage { get; set; }
[JsonProperty("per_page")]
public long PerPage { get; set; }
[JsonProperty("pages")]
public long Pages { get; set; }
}
我使用Humanizer进行复数化。
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (member.GetCustomAttribute<JsonPropertyNameBasedOnItemClassAttribute>() != null)
{
Type[] arguments = property.DeclaringType.GenericTypeArguments;
if(arguments.Length > 0)
{
string name = arguments[0].Name.ToString();
property.PropertyName = name.ToLower().Pluralize();
}
return property;
}
return base.CreateProperty(member, memberSerialization);
}