鉴于网上的此类文章讲的不那么好,特在此重新讲一下
创建一个.Net Core控制台程序,本文代码需要Nuget包Newtonsoft。安装后就可以开始了
首先交代一下使用的类
public abstract class EntityBase { public virtual long Id { get; set; } } public class Entity : EntityBase { public override long Id { get; set; } public string Name { get; set; } [MyJsonIgnore] public decimal Money { get; set; } public Category Category { get; set; } public List<Child> Children { get; set; } } public class Child : EntityBase { public string Name { get; set; } } public enum Category { Human = 0, Cat = 1, Dog = 2 }
默认情况下的序列化
public class Program { public static void Main() { var entity = new Entity { Id = 11, Name = "aa", Money = 1000, Category = Category.Human, Children = new List<Child> { new Child{ Id = 22, Name = "bb"} } }; var json = JsonConvert.SerializeObject(entity); } }
结果:
现在我们不想输出Id,并且Name换成"名字"
方案一:使用Newtonsoft的原生特性,适用于所有此类序列化输出都是相同的场景
主要特性
[JsonIgnore]:序列化成字符串时,不带上这个属性
[JsonProperty]:序列化时,修改属性名
如:作为模型的类修改如下
public abstract class EntityBase { public virtual long Id { get; set; } } public class Entity : EntityBase { [JsonIgnore] public override long Id { get; set; } [JsonProperty("名字")] public string Name { get; set; } public decimal Money { get; set; } public Category Category { get; set; } public List<Child> Children { get; set; } } public class Child : EntityBase { [JsonProperty("名字")] public string Name { get; set; } } public enum Category { Human = 0, Cat = 1, Dog = 2 }
然后Main代码不用改
结果
这种方案的好处是简单,坏处就是只有1种输出
方案二:自己实现转化器convertor,适用于任何场景
1、在序列化时,Newtonsoft提供了自定义的方案,只要写一个类去继承Newtonsoft.Json.JsonConvertor类即可。
public class MyConvertor : JsonConverter { public override bool CanConvert(Type objectType) { return true; //反序列化时先执行 } public override bool CanRead => false; //使用默认反序列化 public override bool CanWrite => true; public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); //反序列化代码 } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var jObject = new JObject(); var entity = value as Entity; var type = typeof(Entity); var props = type.GetProperties(); foreach (var prop in props) { var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault(); if (att != null) { continue; } var name = prop.Name; var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault(); if (att2 != null) { name = ((MyJsonPropertyAttribute) att2).PropertyName; } var v = prop.GetValue(entity); jObject.Add(name, JToken.FromObject(v)); } jObject.WriteTo(writer); } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class MyJsonPropertyAttribute : Attribute { public string PropertyName { get; } public MyJsonPropertyAttribute(string name) { PropertyName = name; } }
注意1:本文只讨论序列化,不需要反序列化的场景。所以CanRead=false,并且CanConvert跟ReadJson都没有实现,如果想使用默认反序列化方案的,CanConvert返回true,CanConvert是表示使用此Convertor类反序列化时,模型对象能否反序列化,网上许多写法都返回了bool--“当前类是否继承某个类或实现某个接口来判断能否反序列化”,但我认为,既然是默认反序列化方案的话,直接返回true即可,除非你也自定义反序列化方案,那就要判断
public override bool CanConvert(Type objectType) { return objectType.IsAssignableFrom(typeof(EntityBase)); }
注意2:在代码中,我使用了自定义的特性来代替原生的JsonIgnoreAttribute和JsonPropertyAttribute,如果你确定只可能有1种输出但又不想使用默认的序列化方案时,那么可以不自定义特性,用回原生特性(效果都一样)。使用自定义特性是为了当有其他的Convertor输出方案时,可以不一样。比如某个方案需要序列化json时,带上Id字段,某个方案又不带上,还有个方案要求Name显示成「名前」...em...一般极少2种及以上的情况的,所以极少需要自定义特性
注意3:在把生成的JObject对象写到writer中时,网上的博客大多数写法是这样的,各位看官可以验证这种写法最终序列化后会多出一次转义操作,这是错误的。stackoverflow网站上的写法也就是我这种jObject.WriteTo(writer);才是正确的
2、模型类使用特性,参考方案一那样
补充1:可以在模型类上,使用特性来绑定序列化Convertor,表明我的默认序列化方案就是这个Convertor;当JsonConvert.SerializeObject调用时不传convertor就是使用默认方案
[JsonConverter(typeof(MyConvertor))]
补充2:可以使用入参Formatting.Indented来使序列化的json换行
最后我选择继承泛型的JsonConvertor让更多的模型可以使用这个Convertor,如这里的Child类使用特性绑定了MyConvertor为默认序列化方案,全部代码整合如下:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; namespace Json { public class Program { public static void Main() { var entity = new Entity { Id = 11, Name = "aa", Money = 1000, Category = Category.Human, Children = new List<Child> { new Child{ Id = 22, Name = "bb"} } }; var convertor = new MyConvertor<Entity>(); var json = JsonConvert.SerializeObject(entity, Formatting.Indented, convertor); } } public abstract class EntityBase { public virtual long Id { get; set; } } public class Entity : EntityBase { [MyJsonIgnore] public override long Id { get; set; } [MyJsonProperty("なまえ")] public string Name { get; set; } public decimal Money { get; set; } public Category Category { get; set; } public List<Child> Children { get; set; } } [JsonConverter(typeof(MyConvertor<Child>))] public class Child : EntityBase { [MyJsonIgnore] public override long Id { get; set; } [JsonProperty("名字")] public string Name { get; set; } } public enum Category { Human = 0, Cat = 1, Dog = 2 } public class MyConvertor<TEntity> : JsonConverter<TEntity> { public override bool CanRead => false; //使用默认反序列化 public override bool CanWrite => true; public override TEntity ReadJson(JsonReader reader, Type objectType, TEntity existingValue, bool hasExistingValue, JsonSerializer serializer) { throw new NotImplementedException(); //反序列化代码 } public override void WriteJson(JsonWriter writer, TEntity entity, JsonSerializer serializer) { var jObject = new JObject(); var type = typeof(TEntity); var props = type.GetProperties(); foreach (var prop in props) { var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault(); if (att != null) { continue; } var name = prop.Name; var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault(); if (att2 != null) { name = ((MyJsonPropertyAttribute)att2).PropertyName; } var v = prop.GetValue(entity); jObject.Add(name, JToken.FromObject(v)); } jObject.WriteTo(writer); } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class MyJsonPropertyAttribute : Attribute { public string PropertyName { get; } public MyJsonPropertyAttribute(string name) { PropertyName = name; } } }
其实特性的逻辑代码应该在特性的类里面写的,这里不搞这么复杂了,因为笔者的文章面向于初学者
运行结果
实用封装:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Json { public class Program { public static void Main() { var entity = new Entity { Id = 11, Name = "aa", Money = 1000, Category = Category.Human, Children = new List<Child> { new Child{ Id = 22, Name = "bb"}, new Child{ Id = 33, Name = "cc"} } }; var convertor = new MyConvertor(); var json = JsonConvert.SerializeObject(entity, Formatting.Indented, convertor); } } public abstract class EntityBase { public virtual long Id { get; set; } } public class Entity : EntityBase { [MyJsonIgnore] public override long Id { get; set; } [MyJsonProperty("なまえ")] public string Name { get; set; } public decimal Money { get; set; } public Category Category { get; set; } [MyJsonEntity(true)] public List<Child> Children { get; set; } } public class Child : EntityBase { [MyJsonIgnore] public override long Id { get; set; } [JsonProperty("名字")] public string Name { get; set; } } public enum Category { Human = 0, Cat = 1, Dog = 2 } public class MyConvertor : JsonConverter { public override bool CanRead => false; //使用默认反序列化 public override bool CanWrite => true; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var jObject = new JObject(); var type = value.GetType(); var props = type.GetProperties(); foreach (var prop in props) { var att = prop.GetCustomAttributes(typeof(MyJsonIgnoreAttribute), false).FirstOrDefault(); if (att != null) { continue; } var name = prop.Name; var att2 = prop.GetCustomAttributes(typeof(MyJsonPropertyAttribute), false).FirstOrDefault(); if (att2 != null) { name = ((MyJsonPropertyAttribute)att2).PropertyName; } var v = prop.GetValue(value); if (v == null) { jObject.Add(name, null); continue; } var att3 = prop.GetCustomAttributes(typeof(MyJsonEntityAttribute), false).FirstOrDefault(); if (att3 != null) { var a3 = (MyJsonEntityAttribute)att3; if (a3.IsList) { var vs = (IList)v; var tempV = new List<object>(); foreach (var t in vs) { var temp = JsonConvert.SerializeObject(t, this); tempV.Add(JsonConvert.DeserializeObject(temp)); } v = tempV; } else { var temp = JsonConvert.SerializeObject(v, this); v = JsonConvert.DeserializeObject(temp); } } jObject.Add(name, JToken.FromObject(v)); } jObject.WriteTo(writer); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override bool CanConvert(Type objectType) { return true; } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class MyJsonPropertyAttribute : Attribute { public string PropertyName { get; } public MyJsonPropertyAttribute(string name) { PropertyName = name; } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonEntityAttribute : Attribute { public bool IsList { get; } public MyJsonEntityAttribute(bool isList) { IsList = isList; } } }