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