鑒於網上的此類文章講的不那么好,特在此重新講一下
創建一個.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; using System.Reflection; namespace Json { public class Program { public static void Main() { var entity = new Entity { Id = 11, Name = null, Category = Category.Human, Children = new List<Child> { new Child{ Id = 22, Name = "bb"}, new Child{ Id = 33, Name = "cc"} } }; var entity2 = new Entity { Id = 11, Name = "aa", Money = 1000, Category = Category.Human }; var entities = new EntityBase[] { entity, entity2 }; var convertor = new MyConvertor(); var json = JsonConvert.SerializeObject(entities, Formatting.Indented, convertor); } } public abstract class EntityBase { [MyJsonIgnore] public virtual long Id { get; set; } } public class Entity : EntityBase { public override long Id { get; set; } [MyJsonProperty("なまえ")] [MyJsonIgnore(true)] public string Name { get; set; } [MyJsonIgnore(true)] 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 } 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 type = value.GetType(); if (type.IsIEnumerable()) { var jArray = new JArray(); var items = (IEnumerable)value; foreach (var item in items) { var temp = JsonConvert.SerializeObject(item, this); jArray.Add(JsonConvert.DeserializeObject(temp)); } jArray.WriteTo(writer); } else { var jObject = new JObject(); var props = type.GetProperties(); foreach (var prop in props) { var ignoreAttribute = prop.GetAttribute<MyJsonIgnoreAttribute>(); if (ignoreAttribute != null && !ignoreAttribute.OnlyNull) { continue; } var name = prop.Name; var propertyAttribute = prop.GetAttribute<MyJsonPropertyAttribute>(); if (propertyAttribute != null) { name = propertyAttribute.PropertyName; } object val; if (prop.PropertyType.IsFundamental()) { val = prop.GetValue(value); } else { var propValue = prop.GetValue(value); var temp = JsonConvert.SerializeObject(propValue, this); val = JsonConvert.DeserializeObject(temp); } if (val == null) { if (ignoreAttribute == null) { jObject.Add(name, null); } continue; } jObject.Add(name, JToken.FromObject(val)); } 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; } } public static class AttributeExtension { public static TAttribute GetAttribute<TAttribute>(this PropertyInfo prop) where TAttribute : Attribute { var obj = prop.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault(); var result = (TAttribute)obj; return result; } } public static class TypeExtension { /// <summary> /// 判斷類型是否是基礎類型 /// </summary> /// <param name="type"></param> /// <returns></returns> public static bool IsFundamental(this Type type) { if (type.IsNullable()) { return IsFundamental(Nullable.GetUnderlyingType(type)); } var result = type.IsPrimitive || type == typeof(decimal) || type == typeof(string) || type.IsEnum || type == typeof(DateTime) || type == typeof(TimeSpan); return result; } /// <summary> /// 判斷類型是否是列表或數組 /// </summary> /// <param name="type"></param> /// <returns></returns> public static bool IsIEnumerable(this Type type) { var result = typeof(IEnumerable).IsAssignableFrom(type); return result; } /// <summary> /// 是否是可空類型 /// </summary> /// <param name="type"></param> /// <returns></returns> public static bool IsNullable(this Type type) { var result = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); return result; } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonIgnoreAttribute : Attribute { public bool OnlyNull { get; set; } public MyJsonIgnoreAttribute(bool onlyNull = false) { OnlyNull = onlyNull; } } [AttributeUsage(AttributeTargets.Property)] public class MyJsonPropertyAttribute : Attribute { public string PropertyName { get; } public MyJsonPropertyAttribute(string name) { PropertyName = name; } } }