using NCC.ClayObject.Extensions; using NCC.Dependency; using NCC.JsonSerialization; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Json; using System.Text; using System.Text.Json; using System.Xml; using System.Xml.Linq; namespace NCC.ClayObject { /// /// 粘土对象 /// [SuppressSniffer] public class Clay : DynamicObject { /// /// 构造函数 /// public Clay() { XmlElement = new XElement("root", CreateTypeAttr(JsonType.@object)); jsonType = JsonType.@object; } /// /// 构造函数 /// /// /// private Clay(XElement element, JsonType type) { Debug.Assert(type == JsonType.array || type == JsonType.@object); XmlElement = element; jsonType = type; } /// /// 是否是 Object 类型 /// public bool IsObject => jsonType == JsonType.@object; /// /// 是否是 Array 类型 /// public bool IsArray => jsonType == JsonType.array; /// /// XML 元素 /// public XElement XmlElement { get; private set; } /// /// 创建一个超级类型 /// /// public static dynamic Object() { return new Clay(); } /// /// 基于现有类型创建一个超级类型 /// /// /// public static dynamic Object(object obj) { return Parse(Serialize(obj)); } /// /// 将 Json 转换成动态类型 /// /// /// public static dynamic Parse(string json) { return Parse(json, Encoding.Unicode); } /// /// 将 Json 转换成动态类型 /// /// /// /// public static dynamic Parse(string json, Encoding encoding) { using var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max); return ToValue(XElement.Load(reader)); } /// /// 将 Steam 转换成动态类型 /// /// /// public static dynamic Parse(Stream stream) { using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max); return ToValue(XElement.Load(reader)); } /// /// 将 Steam 转换成动态类型 /// /// /// /// public static dynamic Parse(Stream stream, Encoding encoding) { using var reader = JsonReaderWriterFactory.CreateJsonReader(stream, encoding, XmlDictionaryReaderQuotas.Max, _ => { }); return ToValue(XElement.Load(reader)); } /// /// 序列化对象 /// /// /// public static string Serialize(object obj) { return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj))); } /// /// 是否定义某个键 /// /// /// public bool IsDefined(string name) { return IsObject && (XmlElement.Element(name) != null); } /// /// 判断数组索引是否存在 /// /// /// public bool IsDefined(int index) { return IsArray && (XmlElement.Elements().ElementAtOrDefault(index) != null); } /// /// 删除键 /// /// /// public bool Delete(string name) { var elem = XmlElement.Element(name); if (elem != null) { elem.Remove(); return true; } else return false; } /// /// 根据索引删除元素 /// /// /// public bool Delete(int index) { var elem = XmlElement.Elements().ElementAtOrDefault(index); if (elem != null) { elem.Remove(); return true; } else return false; } /// /// 反序列化 /// /// /// public T Deserialize() { return (T)Deserialize(typeof(T)); } /// /// 删除 /// /// /// /// /// public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) { result = (IsArray) ? Delete((int)args[0]) : Delete((string)args[0]); return true; } /// /// 判断是否定义 /// /// /// /// /// public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (args.Length > 0) { result = null; return false; } result = IsDefined(binder.Name); return true; } /// /// 支持 Foreach 遍历 /// /// /// /// public override bool TryConvert(ConvertBinder binder, out object result) { if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[])) { var ie = (IsArray) ? XmlElement.Elements().Select(x => ToValue(x)) : XmlElement.Elements().Select(x => (dynamic)new KeyValuePair(x.Name.LocalName, ToValue(x))); result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie; } else { result = Deserialize(binder.Type); } return true; } /// /// 获取索引值 /// /// /// /// /// public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { return (IsArray) ? TryGet(XmlElement.Elements().ElementAtOrDefault((int)indexes[0]), out result) : TryGet(XmlElement.Element((string)indexes[0]), out result); } /// /// 获取成员值 /// /// /// /// public override bool TryGetMember(GetMemberBinder binder, out object result) { return (IsArray) ? TryGet(XmlElement.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result) : TryGet(XmlElement.Element(binder.Name), out result); } /// /// 设置索引 /// /// /// /// /// public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { return (IsArray) ? TrySet((int)indexes[0], value) : TrySet((string)indexes[0], value); } /// /// 设置成员 /// /// /// /// public override bool TrySetMember(SetMemberBinder binder, object value) { return (IsArray) ? TrySet(int.Parse(binder.Name), value) : TrySet(binder.Name, value); } /// /// 获取动态成员名称 /// /// public override IEnumerable GetDynamicMemberNames() { return (IsArray) ? XmlElement.Elements().Select((x, i) => i.ToString()) : XmlElement.Elements().Select(x => x.Name.LocalName); } /// /// 重写 .ToString() /// /// public override string ToString() { // is can't serialize. replace to foreach (var elem in XmlElement.Descendants().Where(x => x.Attribute("type").Value == "null")) { elem.RemoveNodes(); } return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), XmlElement.Elements())); } /// /// 固化粘土,也就是直接输出对象 /// /// public object Solidify() { return Solidify(); } /// /// 固化粘土,也就是直接输出对象 /// /// /// public T Solidify() { return JSON.Deserialize(ToString()); } /// /// 输出字典类型 /// /// public IDictionary ToDictionary() { return Solidify().ToDictionary(); } /// /// JSON 类型 /// private enum JsonType { @string, number, boolean, @object, array, @null } /// /// XElement 转动态类型 /// /// /// private static dynamic ToValue(XElement element) { var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value); return type switch { JsonType.boolean => (bool)element, JsonType.number => (double)element, JsonType.@string => (string)element, JsonType.@object or JsonType.array => new Clay(element, type), _ => null, }; } /// /// 获取 JSON 类型 /// /// /// private static JsonType GetJsonType(object obj) { if (obj == null) return JsonType.@null; var objType = obj.GetType(); // 将特别类型转换成 string if (ToBeConvertStringTypes.Contains(objType)) return JsonType.@string; // 处理循环 Clay 类型 if (obj is ExpandoObject) return JsonType.@object; return Type.GetTypeCode(objType) switch { TypeCode.Boolean => JsonType.boolean, TypeCode.String or TypeCode.Char or TypeCode.DateTime => JsonType.@string, TypeCode.Int16 or TypeCode.Int32 or TypeCode.Int64 or TypeCode.UInt16 or TypeCode.UInt32 or TypeCode.UInt64 or TypeCode.Single or TypeCode.Double or TypeCode.Decimal or TypeCode.SByte or TypeCode.Byte => JsonType.number, TypeCode.Object => (obj is IEnumerable) ? JsonType.array : JsonType.@object, _ => JsonType.@null, }; } /// /// 创建类型属性 /// /// /// private static XAttribute CreateTypeAttr(JsonType type) { return new XAttribute("type", type.ToString()); } /// /// 创建 JSON 节点 /// /// /// private static object CreateJsonNode(object obj) { var type = GetJsonType(obj); return type switch { JsonType.@string or JsonType.number => obj, JsonType.boolean => obj.ToString().ToLower(), JsonType.@object => CreateXObject(obj), JsonType.array => CreateXArray(obj as IEnumerable), _ => null, }; } /// /// 创建 XStreamingElement 对象 /// /// /// /// private static IEnumerable CreateXArray(T obj) where T : IEnumerable { return obj.Cast() .Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o))); } /// /// 创建 XStreamingElement 对象 /// /// /// private static IEnumerable CreateXObject(object obj) { if (obj is ExpandoObject expando) { var dict = (IDictionary)expando; return dict.Select(a => new XStreamingElement(a.Key, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value))); } return obj.GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Select(pi => new { pi.Name, Value = pi.GetValue(obj, null) }) .Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value))); } /// /// 创建 JSON 字符串 /// /// /// private static string CreateJsonString(XStreamingElement element) { using var ms = new MemoryStream(); using var writer = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.Unicode); element.WriteTo(writer); writer.Flush(); return Encoding.Unicode.GetString(ms.ToArray()); } /// /// JSON 类型 /// private readonly JsonType jsonType; /// /// 读取值 /// /// /// /// private static bool TryGet(XElement element, out object result) { if (element == null) { result = null; return false; } result = ToValue(element); return true; } /// /// 设置值 /// /// /// /// private bool TrySet(string name, object value) { var type = GetJsonType(value); // 处理循环 Clay 类型 if (value is Clay clay) { if (clay.IsObject) value = value.ToExpandoObject(); else if (clay.IsArray) { var list = new List(); foreach (var item in (dynamic)clay) { list.Add(item is Clay c ? c.ToExpandoObject() : item); } value = list; } } var element = XmlElement.Element(name); if (element == null) { XmlElement.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value))); } else { element.Attribute("type").Value = type.ToString(); element.ReplaceNodes(CreateJsonNode(value)); } return true; } /// /// 设置值 /// /// /// /// private bool TrySet(int index, object value) { var type = GetJsonType(value); var e = XmlElement.Elements().ElementAtOrDefault(index); if (e == null) { XmlElement.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value))); } else { e.Attribute("type").Value = type.ToString(); e.ReplaceNodes(CreateJsonNode(value)); } return true; } /// /// 反序列化 /// /// /// private object Deserialize(Type type) { return (IsArray) ? DeserializeArray(type) : DeserializeObject(type); } /// /// 反序列化值 /// /// /// /// private static dynamic DeserializeValue(XElement element, Type elementType) { var value = ToValue(element); if (value is Clay json) { value = json.Deserialize(elementType); } return NCC.Extensions.ObjectExtensions.ChangeType(value, elementType); } /// /// 反序列化对象 /// /// /// private object DeserializeObject(Type targetType) { var result = Activator.CreateInstance(targetType); var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanWrite) .ToDictionary(pi => pi.Name, pi => pi); foreach (var item in XmlElement.Elements()) { if (!dict.TryGetValue(item.Name.LocalName, out var propertyInfo)) continue; var value = Clay.DeserializeValue(item, propertyInfo.PropertyType); propertyInfo.SetValue(result, value, null); } return result; } /// /// 序列化数组 /// /// /// private object DeserializeArray(Type targetType) { if (targetType.IsArray) { var elemType = targetType.GetElementType(); dynamic array = Array.CreateInstance(elemType, XmlElement.Elements().Count()); var index = 0; foreach (var item in XmlElement.Elements()) { array[index++] = Clay.DeserializeValue(item, elemType); } return array; } else { var elemType = targetType.GetGenericArguments()[0]; dynamic list = Activator.CreateInstance(targetType); foreach (var item in XmlElement.Elements()) { list.Add(Clay.DeserializeValue(item, elemType)); } return list; } } /// /// 将被转换成字符串的类型 /// private static readonly Type[] ToBeConvertStringTypes = new[] { typeof(DateTimeOffset) }; } }