183 lines
6.4 KiB
C#
183 lines
6.4 KiB
C#
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<T>(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<XElement> 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<T>(XElement node, string subname, Func<XElement, T> func)
|
|
{
|
|
var ele = node.Elements().FirstOrDefault(e => string.Equals(e.Name.ToString(), subname, StringComparison.OrdinalIgnoreCase));
|
|
return func(ele);
|
|
}
|
|
|
|
private static T ReadObject<T>(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<T>(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<T>(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<T>(XElement parent, string name, T value) where T : IFormattable => WriteString(parent, name, ToString(value));
|
|
protected static XElement WriteObject<T>(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<T>(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<T>(XElement node, string subname, T def = default) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadObject<T>(e));
|
|
protected static T[] ReadArray<T>(XElement node, string subname, T[] def = null) where T : IModel, new() => ReadSubnode(node, subname, e => e == null ? def : ReadArray<T>(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;
|
|
}
|
|
}
|
|
}
|