/*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*\ ( ( ) ) |/ \| ) ) _((_ || (c) Wanzyee Studio < wanzyeestudio.blogspot.com > || ( ( |_ _ |=n |\ /| _____)) | ! ] U \.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ./ (_(__(S) |___*/ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace WanzyeeStudio.Json{ /// /// Custom base Newtonsoft.Json.JsonConverter to filter serialized properties. /// /// /// /// Useful for Unity or 3rd party classes, since we can't insert any Newtonsoft.Json.JsonIgnoreAttribute. /// By the way, this works by reflection to access properties. /// Please make sure your property not to be stripped by Unity. /// /// /// /// It's very easy to make a custom converter, just inherit and override GetPropertyNames() as the filter: /// /// /// /// public class SomeConverter : PartialConverter{ /// protected override string[] GetPropertyNames(){ /// return new []{"someField", "someProperty", "etc"}; /// } /// } /// /// public abstract class PartialConverter : JsonConverter{ #region Static Methods /// /// Get the field or property of the specified name. /// /// The member. /// Name. private static MemberInfo GetMember(string name){ var _flag = BindingFlags.Instance | BindingFlags.Public; var _field = typeof(T).GetField(name, _flag); if(null != _field) return _field; var _property = typeof(T).GetProperty(name, _flag); if(null == _property) Throw(name, "Public instance field or property {0} is not found."); if(null == _property.GetGetMethod()) Throw(name, "Property {0} is not readable."); if(null == _property.GetSetMethod()) Throw(name, "Property {0} is not writable."); if(_property.GetIndexParameters().Any()) Throw(name, "Not support property {0} with indexes."); return _property; } /// /// Throw an exception of the specified message formatted with the member name. /// /// Name. /// Format. private static void Throw(string name, string format){ throw new ArgumentException(string.Format(format, typeof(T).Name + "." + name), "name"); } /// /// Get the value from the member. /// /// The value. /// Member. /// Target. private static object GetValue(MemberInfo member, object target){ if(member is FieldInfo) return (member as FieldInfo).GetValue(target); else return (member as PropertyInfo).GetValue(target, null); } /// /// Set the value to the member. /// /// Member. /// Target. /// Value. private static void SetValue(MemberInfo member, object target, object value){ if(member is FieldInfo) (member as FieldInfo).SetValue(target, value); else (member as PropertyInfo).SetValue(target, value, null); } /// /// Get the value type of the member. /// /// The value type. /// Member. private static Type GetValueType(MemberInfo member){ if(member is FieldInfo) return (member as FieldInfo).FieldType; else return (member as PropertyInfo).PropertyType; } #endregion #region Fields /// /// The stored property names with the member. /// private static Dictionary _properties; #endregion #region Methods /// /// Gets the property names paired with the accessing member. /// /// The properties. private Dictionary GetProperties(){ if(null != _properties) return _properties; var _names = GetPropertyNames(); if(null == _names || !_names.Any()) throw new InvalidProgramException("GetPropertyNames() cannot return empty."); if(_names.Any((name) => string.IsNullOrEmpty(name))) throw new InvalidProgramException("GetPropertyNames() cannot contain empty value."); _properties = _names.Distinct().ToDictionary((name) => name, (name) => GetMember(name)); return _properties; } /// /// Get the property names to serialize, only used once when initializing. /// /// The property names. protected abstract string[] GetPropertyNames(); /// /// Create the instance for ReadJson() to populate. /// /// The instance. protected virtual T CreateInstance(){ return Activator.CreateInstance(); } /// /// Determine if the object type is T. /// /// Type of the object. /// true if this can convert the specified type; otherwise, false. public override bool CanConvert(Type objectType){ return typeof(T) == objectType; } /// /// Read the specified properties to the object. /// /// The object value. /// The Newtonsoft.Json.JsonReader to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. /* * Force the instance as an object reference, otherwise this may reflect to a wrong copy if the T is struct. * But keep the CreateInstance() to return T for safer overriding. */ public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ){ if(JsonToken.Null == reader.TokenType) return null; var _object = JObject.Load(reader); var _result = CreateInstance() as object; foreach(var _pair in GetProperties()){ var _value = _object[_pair.Key].ToObject(GetValueType(_pair.Value), serializer); SetValue(_pair.Value, _result, _value); } return _result; } /// /// Write the specified properties of the object. /// /// The Newtonsoft.Json.JsonWriter to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer){ var _object = new JObject(); foreach(var _pair in GetProperties()){ var _value = GetValue(_pair.Value, value); _object[_pair.Key] = JToken.FromObject(_value, serializer); } _object.WriteTo(writer); } #endregion } }