using System; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; namespace Billing.Models; public interface IModel { void OnXmlSerialize(XElement node); void OnXmlDeserialize(XElement node); } public abstract class BaseModel : IModel, IDisposable { private bool disposed = false; public static T ParseXml(string xml) where T : BaseModel, new() { XDocument doc = XDocument.Parse(xml); T model = new(); model.OnXmlDeserialize(doc.Root); return model; } protected static string ToString(IFormattable v) => v.ToString(null, CultureInfo.InvariantCulture); protected static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture); protected static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture); protected static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture); protected static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture); protected static bool IsTrue(string s) => string.Equals(s, "true", StringComparison.OrdinalIgnoreCase) || string.Equals(s, "yes", StringComparison.OrdinalIgnoreCase) || s == "1"; #region Private Methods private static XElement WriteString(XElement parent, string name, string val, Action action = null) { XElement ele; if (val == null) { ele = new XElement(name); } else { ele = new XElement(name, val); } action?.Invoke(ele); parent.Add(ele); return ele; } private static T ReadSubnode(XElement node, string subname, Func func) { var ele = node.Elements().FirstOrDefault(e => string.Equals(e.Name.ToString(), subname, StringComparison.OrdinalIgnoreCase)); return func(ele); } private static T ReadObject(XElement node) where T : IModel, new() { if (IsTrue(node.Attribute("null")?.Value)) { return default; } T value = new(); value.OnXmlDeserialize(node); return value; } private static T[] ReadArray(XElement node) where T : IModel, new() { if (IsTrue(node.Attribute("null")?.Value)) { return default; } int count = ParseInt(node.Attribute("count").Value); T[] array = new T[count]; foreach (XElement ele in node.Elements("item")) { int index = ParseInt(ele.Attribute("index").Value); array[index] = ReadObject(ele); } return array; } #endregion #region Read/Write Helper protected static XElement Write(XElement parent, string name, string val) => WriteString(parent, name, val); protected static XElement Write(XElement parent, string name, DateTime date) => WriteString(parent, name, ToString(date.Ticks)); protected static XElement Write(XElement parent, string name, T value) where T : IFormattable => WriteString(parent, name, ToString(value)); protected static XElement WriteObject(XElement parent, string name, T value) where T : IModel => WriteString(parent, name, null, action: ele => { if (value == null) { ele.Add(new XAttribute("null", 1)); } else { value.OnXmlSerialize(ele); } }); protected static XElement WriteArray(XElement parent, string name, T[] value) where T : IModel => WriteString(parent, name, null, action: ele => { if (value == null) { ele.Add(new XAttribute("null", 1)); } else { ele.Add(new XAttribute("count", value.Length)); for (var i = 0; i < value.Length; i++) { XElement item = WriteObject(ele, "item", value[i]); item.Add(new XAttribute("index", i)); } } }); protected static string Read(XElement node, string subname, string def) => ReadSubnode(node, subname, e => e?.Value ?? def); protected static int Read(XElement node, string subname, int def) => ReadSubnode(node, subname, e => e == null ? def : ParseInt(e.Value)); protected static long Read(XElement node, string subname, long def) => ReadSubnode(node, subname, e => e == null ? def : ParseLong(e.Value)); protected static double Read(XElement node, string subname, double def) => ReadSubnode(node, subname, e => e == null ? def : ParseDouble(e.Value)); protected static decimal Read(XElement node, string subname, decimal def) => ReadSubnode(node, subname, e => e == null ? def : ParseDecimal(e.Value)); protected static DateTime Read(XElement node, string subname, DateTime def) => ReadSubnode(node, subname, e => e == null ? def : new DateTime(ParseLong(e.Value))); protected static T ReadObject(XElement node, string subname, T def = default) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadObject(e)); protected static T[] ReadArray(XElement node, string subname, T[] def = null) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadArray(e)); #endregion public XDocument ToXml() { XDocument xdoc = new(new XDeclaration("1.0", "utf-8", "yes"), new XElement("root")); ToXml(xdoc.Root); return xdoc; } public void ToXml(XElement node) { OnXmlSerialize(node); } public void SaveToStream(Stream stream) { ToXml().Save(stream, SaveOptions.DisableFormatting); } public abstract void OnXmlSerialize(XElement node); public abstract void OnXmlDeserialize(XElement node); public override string ToString() { XDocument xdoc = ToXml(); using MemoryStream ms = new(); using StreamWriter writer = new(ms, Encoding.UTF8); xdoc.Save(writer, SaveOptions.DisableFormatting); writer.Flush(); ms.Seek(0, SeekOrigin.Begin); using StreamReader reader = new(ms, Encoding.UTF8); return reader.ReadToEnd(); } protected virtual void Dispose(bool dispose) { } public void Dispose() { if (!disposed) { Dispose(true); disposed = true; } } }