YanCheng_Metrology/Assets/Plugins/WanzyeeStudio/Scripts/Runtime/Concrete/Json/PartialConverter.cs

233 lines
7.0 KiB
C#

/*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{
/// <summary>
/// Custom base <c>Newtonsoft.Json.JsonConverter</c> to filter serialized properties.
/// </summary>
///
/// <remarks>
/// Useful for Unity or 3rd party classes, since we can't insert any <c>Newtonsoft.Json.JsonIgnoreAttribute</c>.
/// By the way, this works by reflection to access properties.
/// Please make sure your property not to be stripped by Unity.
/// </remarks>
///
/// <example>
/// It's very easy to make a custom converter, just inherit and override <c>GetPropertyNames()</c> as the filter:
/// </example>
///
/// <code>
/// public class SomeConverter : PartialConverter<SomeClass>{
/// protected override string[] GetPropertyNames(){
/// return new []{"someField", "someProperty", "etc"};
/// }
/// }
/// </code>
///
public abstract class PartialConverter<T> : JsonConverter{
#region Static Methods
/// <summary>
/// Get the field or property of the specified <c>name</c>.
/// </summary>
/// <returns>The member.</returns>
/// <param name="name">Name.</param>
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;
}
/// <summary>
/// Throw an exception of the specified message formatted with the member name.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="format">Format.</param>
private static void Throw(string name, string format){
throw new ArgumentException(string.Format(format, typeof(T).Name + "." + name), "name");
}
/// <summary>
/// Get the value from the member.
/// </summary>
/// <returns>The value.</returns>
/// <param name="member">Member.</param>
/// <param name="target">Target.</param>
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);
}
/// <summary>
/// Set the value to the member.
/// </summary>
/// <param name="member">Member.</param>
/// <param name="target">Target.</param>
/// <param name="value">Value.</param>
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);
}
/// <summary>
/// Get the value type of the member.
/// </summary>
/// <returns>The value type.</returns>
/// <param name="member">Member.</param>
private static Type GetValueType(MemberInfo member){
if(member is FieldInfo) return (member as FieldInfo).FieldType;
else return (member as PropertyInfo).PropertyType;
}
#endregion
#region Fields
/// <summary>
/// The stored property names with the member.
/// </summary>
private static Dictionary<string, MemberInfo> _properties;
#endregion
#region Methods
/// <summary>
/// Gets the property names paired with the accessing member.
/// </summary>
/// <returns>The properties.</returns>
private Dictionary<string, MemberInfo> 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;
}
/// <summary>
/// Get the property names to serialize, only used once when initializing.
/// </summary>
/// <returns>The property names.</returns>
protected abstract string[] GetPropertyNames();
/// <summary>
/// Create the instance for <c>ReadJson()</c> to populate.
/// </summary>
/// <returns>The instance.</returns>
protected virtual T CreateInstance(){
return Activator.CreateInstance<T>();
}
/// <summary>
/// Determine if the object type is <c>T</c>.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns><c>true</c> if this can convert the specified type; otherwise, <c>false</c>.</returns>
public override bool CanConvert(Type objectType){
return typeof(T) == objectType;
}
/// <summary>
/// Read the specified properties to the object.
/// </summary>
/// <returns>The object value.</returns>
/// <param name="reader">The <c>Newtonsoft.Json.JsonReader</c> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/*
* 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;
}
/// <summary>
/// Write the specified properties of the object.
/// </summary>
/// <param name="writer">The <c>Newtonsoft.Json.JsonWriter</c> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
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
}
}