using System; using System.Text; using System.IO; using System.Xml; using System.Reflection; using System.Collections; using System.Collections.Generic; using CommonLang.Property; using CommonLang.IO; using CommonLang.Log; using CommonLang.Concurrent; namespace CommonLang.Xml { public enum XmlSerializableProperty : uint { Mark = 0, IgnoreClone = 0x0001, NoSerialize = 0x0002, } /// /// 标记当前Field或者Property是否参与Xml序列化 /// [AttributeUsage(AttributeTargets.Property)] public class XmlSerializableAttribute : System.Attribute { private readonly XmlSerializableProperty Prop; public XmlSerializableAttribute(XmlSerializableProperty attr = XmlSerializableProperty.Mark) { this.Prop = attr; } public bool IgnoreClone { get { return (Prop & XmlSerializableProperty.IgnoreClone) != 0; } } public bool NoSerialize { get { return (Prop & XmlSerializableProperty.NoSerialize) != 0; } } } /// /// Xml序列化与反序列化 /// public static class XmlUtil { private static Logger s_log; private static Logger log { get { if (s_log == null) { s_log = LoggerFactory.GetLogger("XmlUtil"); } return s_log; } } static public XmlDocument LoadXML(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { return LoadXML(ms); } } static public XmlDocument LoadXML(Stream input, bool autoDisposeStream = false) { try { using (XmlReader xml = XmlReader.Create(input)) { XmlDocument doc = new XmlDocument(); doc.Load(xml); return doc; } } finally { if (autoDisposeStream) { input.Close(); input.Dispose(); } } } static public XmlDocument LoadXML(string path) { var stream = Resource.LoadDataAsStream(path); if (stream != null) { try { return LoadXML(stream); } finally { stream.Dispose(); } } return null; } static public void SaveXML(string path, XmlDocument xml) { using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) { XmlUtil.SaveXML(fs, xml, false); } } static public string ToString(XmlDocument doc) { using (StringWriter sw = new StringWriter()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = CUtils.UTF8; using (XmlWriter xml = XmlWriter.Create(sw, settings)) { doc.Save(xml); xml.Flush(); } return sw.ToString(); } } static public XmlDocument FromString(string xmltext) { XmlDocument doc = new XmlDocument(); doc.LoadXml(xmltext); return doc; } static public bool SaveXML(String path, XmlDocument doc, out string errMessage) { try { using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = Encoding.UTF8; using (XmlWriter xml = XmlWriter.Create(fs, settings)) { doc.Save(xml); xml.Flush(); } errMessage = null; fs.Close(); } } catch(Exception e) { errMessage = e.Message; } return errMessage == null; } static public void SaveXML(Stream output, XmlDocument doc, bool autoDisposeStream) { try { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = Encoding.UTF8; using (XmlWriter xml = XmlWriter.Create(output, settings)) { doc.Save(xml); xml.Flush(); } } finally { if (autoDisposeStream) { output.Close(); output.Dispose(); } } } static public string GetTextValue(XmlElement e) { if (e.FirstChild != null && e.FirstChild.NodeType == XmlNodeType.Text) { return (e.FirstChild as XmlText).Data; } else { return null; } } static public void SaveToXML(Stream output, object mData, bool autoDisposeStream = false) { try { Type type = mData.GetType(); XmlDocument doc = XmlUtil.ObjectToXml(mData); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.Encoding = Encoding.UTF8; using (XmlWriter xml = XmlWriter.Create(output, settings)) { doc.Save(xml); xml.Flush(); } } finally { if (autoDisposeStream) { output.Close(); output.Dispose(); } } } //---------------------------------------------------------------------------------------------------------- #region _converter_ static public T XmlToObject(XmlDocument doc, Action error = null) { object ret = XmlToObject(typeof(T), doc, false, error); if (ret != null) { return (T)ret; } return default(T); } static public object XmlToObject(XmlDocument doc, Action error = null) { XmlElement e = (XmlElement)doc.DocumentElement; Type type = GetTypeFromAttribute(e, "type"); return XmlToObject(type, doc, false, error); } static public object XmlToObject(Type type, XmlDocument doc, Action error = null) { XmlElement e = (XmlElement)doc.DocumentElement; return XmlToObject(type, doc, false, error); } static public object XmlToObject(Type type, XmlDocument doc, bool cloning, Action error = null) { XmlElement e = (XmlElement)doc.DocumentElement; object data = ObjectFromXmlElement(e, type, cloning, error); return data; } static public XmlDocument ObjectToXml(object data, string root_name = null) { return ObjectToXml(data, root_name, false); } static public XmlDocument ObjectToXml(object data, string root_name, bool cloning) { Type type = data.GetType(); if (root_name == null) { root_name = type.Name; } XmlDocument doc = new XmlDocument(); XmlElement e = doc.CreateElement(root_name); ObjectToXmlElement(e, data, cloning); doc.AppendChild(e); return doc; } static public T CloneObject(T src) { Type type = src.GetType(); if (type.IsPrimitive) { return src; } else if (type.IsEnum) { return src; } else if (type.IsAssignableFrom(typeof(string))) { return src; } else if (type.IsClass || type.IsArray) { XmlDocument xml = XmlUtil.ObjectToXml(src, "cloning", true); T obj = (T)XmlUtil.XmlToObject(type, xml, true); return obj; } return src; } #endregion //---------------------------------------------------------------------------------------------------------- #region _internal_ static private void ObjectFieldsFromXMLElement(XmlElement data_element, object data, bool cloning, Action error) { Type type = data.GetType(); foreach (XmlNode fe in data_element.ChildNodes) { XmlElement fee = fe as XmlElement; FieldInfo fii = type.GetField(fe.Name); if (fii != null) { object fd = ObjectFromXmlElement(fee, fii.FieldType, cloning, error); fii.SetValue(data, fd); } } } static private void ObjectToXmlElement(XmlElement data_element, object data, bool cloning) { if (data != null) { Type type = data.GetType(); if (type.IsPrimitive) { data_element.InnerText = Parser.ObjectToString(data); } else if (type.IsEnum) { data_element.InnerText = Parser.ObjectToString(data); } else if (type.IsAssignableFrom(typeof(string))) { data_element.InnerText = Parser.ObjectToString(data); } else if (type.IsArray) { ObjectToXMLElementArray(data_element, (Array)data, cloning); } else if (type.IsClass) { XmlDocument doc = data_element.OwnerDocument; if (type.GetInterface(typeof(IDictionary).Name) != null) { ObjectToXMLElementMap(data_element, (IDictionary)data, cloning); } else if (type.GetInterface(typeof(IList).Name) != null) { ObjectToXMLElementList(data_element, (IList)data, cloning); } else { SetTypeToAttribute(data_element, "type", type); foreach (FieldInfo field in PropertyUtil.SortFields(type.GetFields())) { if (!field.IsStatic) { object fd = field.GetValue(data); if (fd != null) { XmlElement fe = doc.CreateElement(field.Name); ObjectToXmlElement(fe, fd, cloning); data_element.AppendChild(fe); } } } foreach (PropertyInfo property in PropertyUtil.SortProperties(type.GetProperties())) { XmlSerializableAttribute attr = PropertyUtil.GetAttribute(property); if (attr != null) { if (attr.NoSerialize) { } else if (cloning && attr.IgnoreClone) { } else { object fd = property.GetValue(data, null); if (fd != null) { XmlElement fe = doc.CreateElement("property." + property.Name); ObjectToXmlElement(fe, fd, cloning); data_element.AppendChild(fe); } } } } } } } } static private object ObjectFromXmlElement(XmlElement data_element, Type type, bool cloning, Action error) { try { Type s_type = GetTypeFromAttribute(data_element, "type", type); if (s_type != null) { type = s_type; } if (type.IsPrimitive) { return Parser.StringToObject(data_element.InnerText, type); } else if (type.IsEnum) { return Parser.StringToObject(data_element.InnerText, type); } else if (type.IsAssignableFrom(typeof(string))) { return data_element.InnerText; } else if (type.IsArray) { return ObjectFromXMLElementArray(data_element, type, cloning, error); } else if (type.IsClass) { if (type.GetInterface(typeof(IDictionary).Name) != null) { return ObjectFromXMLElementMap(data_element, type, cloning, error); } else if (type.GetInterface(typeof(IList).Name) != null) { return ObjectFromXMLElementList(data_element, type, cloning, error); } else { try { object data = ReflectionUtil.CreateInstance(type); foreach (XmlNode fe in data_element.ChildNodes) { XmlElement fee = fe as XmlElement; if (fe.Name.StartsWith("property.")) { PropertyInfo property = type.GetProperty(fe.Name.Substring("property.".Length)); if (property != null) { XmlSerializableAttribute attr = PropertyUtil.GetAttribute(property); if (attr != null) { if (attr.NoSerialize) { } else if (cloning && attr.IgnoreClone) { } else { object fd = ObjectFromXmlElement(fee, property.PropertyType, cloning, error); if (fd != null) { property.SetValue(data, fd, null); } } } } } else { FieldInfo fii = type.GetField(fe.Name); if (fii != null) { object fd = ObjectFromXmlElement(fee, fii.FieldType, cloning, error); if (fd != null) { fii.SetValue(data, fd); } } } } return data; } catch (Exception err) { log.Warn(err.Message, err); if (error != null) error(err); } } } } catch (Exception err) { log.Warn(err.Message, err); if (error != null) error(err); } return null; } static private void ObjectToXMLElementArray(XmlElement data_element, Array array, bool cloning) { XmlDocument doc = data_element.OwnerDocument; Type type = array.GetType(); int rank = type.GetArrayRank(); int[] ranges = new int[rank]; for (int i = 0; i < rank; i++) { ranges[i] = array.GetLength(i); } SetTypeToAttribute(data_element, "type", type); SetTypeToAttribute(data_element, "element_type", type.GetElementType()); data_element.SetAttribute("rank", rank + ""); data_element.SetAttribute("ranges", Parser.ObjectToString(ranges)); foreach (object k in array) { XmlElement ei = doc.CreateElement("element"); ObjectToXmlElement(ei, k, cloning); data_element.AppendChild(ei); } } static private Array ObjectFromXMLElementArray(XmlElement data_element, Type type, bool cloning, Action error) { int rank = int.Parse(data_element.GetAttribute("rank")); Type ctype = GetTypeFromAttribute(data_element, "type"); Type etype = GetTypeFromAttribute(data_element, "element_type"); int[] ranges = (int[])Parser.StringToObject(data_element.GetAttribute("ranges"), typeof(int[])); Array array = Array.CreateInstance(etype, ranges); int total_index = 0; foreach (XmlNode fe in data_element.ChildNodes) { XmlElement fee = fe as XmlElement; object fdd = ObjectFromXmlElement(fee, etype, cloning, error); int[] indices = CUtils.GetArrayRankIndex(ranges, total_index); array.SetValue(fdd, indices); total_index++; } return array; } static private void ObjectToXMLElementMap(XmlElement data_element, IDictionary map, bool cloning) { XmlDocument doc = data_element.OwnerDocument; Type type = map.GetType(); if (type.IsGenericType) { SetTypeToAttribute(data_element, "key_type", type.GetGenericArguments()[0]); SetTypeToAttribute(data_element, "value_type", type.GetGenericArguments()[1]); } foreach (object k in map.Keys) { object v = map[k]; XmlElement epair = doc.CreateElement("element"); XmlElement ek = doc.CreateElement("key"); XmlElement ev = doc.CreateElement("value"); if (!type.IsGenericType) { SetTypeToAttribute(ek, "type", k.GetType()); SetTypeToAttribute(ev, "type", v.GetType()); } ObjectToXmlElement(ek, k, cloning); ObjectToXmlElement(ev, v, cloning); epair.AppendChild(ek); epair.AppendChild(ev); data_element.AppendChild(epair); } } static private IDictionary ObjectFromXMLElementMap(XmlElement data_element, Type type, bool cloning, Action error) { Type k_type = GetTypeFromAttribute(data_element, "key_type"); Type v_type = GetTypeFromAttribute(data_element, "value_type"); IDictionary map = (IDictionary)Activator.CreateInstance(type); foreach (XmlNode fe in data_element.ChildNodes) { XmlElement epair = fe as XmlElement; XmlElement ek = epair.ChildNodes[0] as XmlElement; XmlElement ev = epair.ChildNodes[1] as XmlElement; k_type = GetTypeFromAttribute(ek, "type", k_type); v_type = GetTypeFromAttribute(ev, "type", v_type); object k = ObjectFromXmlElement(ek, k_type, cloning, error); object v = ObjectFromXmlElement(ev, v_type, cloning, error); map.Add(k, v); } return map; } static private void ObjectToXMLElementList(XmlElement data_element, IList list, bool cloning) { XmlDocument doc = data_element.OwnerDocument; Type type = list.GetType(); if (type.IsGenericType) { SetTypeToAttribute(data_element, "element_type", type.GetGenericArguments()[0]); } foreach (object k in list) { XmlElement ei = doc.CreateElement("element"); if (!type.IsGenericType) { SetTypeToAttribute(ei, "type", k.GetType()); } ObjectToXmlElement(ei, k, cloning); data_element.AppendChild(ei); } } static private IList ObjectFromXMLElementList(XmlElement data_element, Type type, bool cloning, Action error) { Type element_type = GetTypeFromAttribute(data_element, "element_type"); IList list = (IList)Activator.CreateInstance(type); foreach (XmlNode fe in data_element.ChildNodes) { XmlElement ei = fe as XmlElement; element_type = GetTypeFromAttribute(ei, "type", element_type); Object fd = ObjectFromXmlElement(ei, element_type, cloning, error); list.Add(fd); } return list; } static private void SetTypeToAttribute(XmlElement e, string name, Type type) { e.SetAttribute(name, type.FullName); } static private Type GetTypeFromAttribute(XmlElement e, string name) { string vtype = e.GetAttribute(name); if (vtype != null) { Type ret = ReflectionUtil.GetType(vtype); if (ret != null) { return ret; } } return null; } static private Type GetTypeFromAttribute(XmlElement e, string name, Type defaultType) { string vtype = e.GetAttribute(name); if (!String.IsNullOrEmpty(vtype)) { Type ret = ReflectionUtil.GetType(vtype); if (ret != null) { return ret; } } return defaultType; } #endregion //---------------------------------------------------------------------------------------------------------- #region _utils_ public static bool TryGetAttribute(XmlNode e, string key, out string value, bool NotNull = true) { XmlAttribute attr = e.Attributes[key]; if (attr != null) { if (NotNull && string.IsNullOrEmpty(attr.Value)) { value = null; return false; } value = attr.Value; return true; } value = null; return false; } public static string GetAttribute(XmlNode e, string key, bool NotNull = true) { XmlAttribute attr = e.Attributes[key]; if (attr != null) { if (NotNull && string.IsNullOrEmpty(attr.Value)) { return null; } return attr.Value; } return null; } public static XmlNode FindChild(XmlNode e, string childName) { foreach (XmlNode cc in e.ChildNodes) { if (cc.Name == childName) { return cc; } } return null; } #endregion //---------------------------------------------------------------------------------------------------- } }