/*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
}
}