using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Data;
using System.IO;
using System.Linq;
using System.Collections;
using EasyDevCore.Common;
namespace EasyDevCore.Database.EntityTable
{
///
///
///
public enum DataTableSerializerSchema
{
///
/// The none
///
None,
///
/// The data table schema
///
DataTableSchema,
///
/// The column schema
///
ColumnSchema
}
///
/// Convert DataTable to XML
///
[Serializable]
public class DataTableSerializer : IXmlSerializable
{
private DataTable _Table;
private DataTableSerializerSchema _ProxySchema = DataTableSerializerSchema.None;
private bool _ForSynchValues = false;
private bool _OptimumSize = true;
private string _KeyFields = string.Empty;
private string _ColumnNames = string.Empty;
private string _ExceptColumnNames = string.Empty;
///
/// use for XmlSerialization
///
private DataTableSerializer()
{
}
///
/// Initializes a new instance of the class.
///
/// The entities.
/// if set to true [only changed values has been serialize for synchronized].
/// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName).
/// The except column names.
/// The key fields.
/// The proxy schema.
/// if set to true [optimum size but slower].
public DataTableSerializer(DataTable entities, bool forSynchValues = false, string columnNames = "", string exceptColumnNames = "", string keyFields = "", DataTableSerializerSchema proxySchema = DataTableSerializerSchema.None, bool optimumSize = true)
{
_Table = entities;
_ForSynchValues = forSynchValues;
_ColumnNames = columnNames;
_ExceptColumnNames = exceptColumnNames;
_KeyFields = keyFields;
_OptimumSize = optimumSize;
_ProxySchema = proxySchema;
if (proxySchema == DataTableSerializerSchema.None && entities != null)
{
_ProxySchema = DataTableSerializerSchema.DataTableSchema;
}
}
///
/// Gets the table.
///
/// The entities.
public virtual DataTable Table
{
get
{
return _Table;
}
}
#region IXmlSerializable Members
///
/// This property is reserved, apply the to the class instead.
///
///
/// An that describes the XML representation of the object that is produced by the method and consumed by the method.
///
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
///
/// Gets the type of the entity collection.
///
///
public virtual Type GetDataTableType()
{
return null;
}
///
/// Loads the XML only for changes.
///
/// The reader.
/// if set to true [optimum size].
/// The map columns.
private void LoadXMLOnlyForSync(System.Xml.XmlReader reader, bool optimumSize, Dictionary mapColumns)
{
DataRow row;
DataColumn column;
string rowState;
while (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "R")
{
row = Table.NewRow();
rowState = reader.GetAttribute("rowStated");
reader.ReadStartElement();
while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement)
{
if (reader.LocalName == "CS") //Special columns
{
string[] colNuls = reader.GetAttribute("N").SplitCommaString();
foreach (string col in colNuls)
{
column = Table.Columns[mapColumns["C" + col]];
row[column.Ordinal] = DBNull.Value;
}
if (optimumSize)
{
string[] colZoEs = reader.GetAttribute("E").SplitCommaString();
foreach (string col in colZoEs)
{
column = Table.Columns[mapColumns["C" + col]];
row[column.Ordinal] = GetZeroOrEmptyValue(column.DataType);
}
}
reader.Read();
}
else
{
column = Table.Columns[mapColumns[reader.LocalName]];
row[column.Ordinal] = reader.ReadElementString().StringTo(column.DataType);
}
}
Table.Rows.Add(row);
switch (rowState)
{
case "Modified":
row.AcceptChanges();
row.SetModified();
break;
case "Deleted":
row.Delete();
break;
}
reader.ReadEndElement();
}
}
///
/// Loads the XML all rows.
///
/// The reader.
/// if set to true [optimum size].
/// The map columns.
private void LoadXMLAllRows(System.Xml.XmlReader reader, bool optimumSize, Dictionary mapColumns)
{
DataRow row;
DataColumn column;
while (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "R")
{
row = Table.NewRow();
reader.ReadStartElement();
while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement)
{
if (reader.LocalName == "CS") //Special columns
{
string colNuls = reader.GetAttribute("N");
if (!string.IsNullOrWhiteSpace(colNuls))
{
foreach (string col in colNuls.SplitCommaString())
{
column = Table.Columns[mapColumns["C" + col]];
row[column.Ordinal] = DBNull.Value;
}
}
if (optimumSize)
{
string colZoEs = reader.GetAttribute("E");
if (!string.IsNullOrWhiteSpace(colZoEs))
{
foreach (string col in colZoEs.SplitCommaString())
{
column = Table.Columns[mapColumns["C" + col]];
row[column.Ordinal] = GetZeroOrEmptyValue(column.DataType);
}
}
}
reader.Read();
}
else
{
column = Table.Columns[mapColumns[reader.LocalName]];
row[column.Ordinal] = reader.ReadElementString().StringTo(column.DataType);
}
}
Table.Rows.Add(row);
reader.ReadEndElement();
}
Table.AcceptChanges();
}
///
/// Generates an object from its XML representation.
///
/// The stream from which the object is deserialized.
public void ReadXml(System.Xml.XmlReader reader)
{
bool forSynchValues = false;
bool includeSchema = false;
string type = string.Empty;
string name = string.Empty;
DataColumnCollection schemaColumns = null;
reader.Read();
if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Option")
{
type = reader.GetAttribute("type");
name = reader.GetAttribute("name");
forSynchValues = reader.GetAttribute("forSynchValues").StringTo(false);
includeSchema = reader.GetAttribute("includeSchema").StringTo(false);
if (Table == null)
{
if (string.IsNullOrEmpty(type) && GetDataTableType() != null)
{
type = GetDataTableType().AssemblyQualifiedName;
}
if (string.IsNullOrEmpty(type))
{
type = typeof(DataTable).AssemblyQualifiedName;
}
_Table = (DataTable)ReflectionHelper.CreateInstance(type);
if (!string.IsNullOrWhiteSpace(name)) _Table.TableName = name;
}
else
{
Table.Clear();
}
string[] extendedProperties = reader.GetAttribute("extendedProperties").Split(new char[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < extendedProperties.Length; i = i + 2)
{
string attributeName = "extP_" + (i / 2).ToString();
string value = reader.GetAttribute(attributeName);
string typeName = extendedProperties[i + 1];
string propertyName = extendedProperties[i];
Table.ExtendedProperties[propertyName] = value.StringTo(extendedProperties[i + 1]);
}
reader.Read();
}
schemaColumns = Table.Columns;
if (includeSchema)
{
if (reader.MoveToContent() == System.Xml.XmlNodeType.Element && reader.LocalName == "Schema")
{
string schemaType = reader.GetAttribute("type");
if (schemaType == DataTableSerializerSchema.ColumnSchema.ToString())
{
reader.ReadStartElement();
while (reader.MoveToContent() != System.Xml.XmlNodeType.EndElement)
{
DataColumn col = Table.Columns.Add(reader.LocalName, Type.GetType(reader.GetAttribute("type")));
string defaultValue = reader.GetAttribute("defaultValue");
col.DefaultValue = defaultValue == "" ? DBNull.Value : defaultValue.StringTo(col.DataType);
reader.Read();
}
reader.ReadEndElement();
}
else //DataTable schema
{
string schema = reader.ReadInnerXml();
if (!string.IsNullOrEmpty(schema))
{
using (StringReader sr = new StringReader(schema))
{
DataTable tempTable = new DataTable(name ?? "DataTable"); //Use only for get schema
tempTable.ReadXmlSchema(sr);
schemaColumns = tempTable.Columns;
}
using (StringReader sr = new StringReader(schema))
{
Table.ReadXmlSchema(sr);
}
}
}
}
}
Dictionary mapColumns = new Dictionary();
foreach (DataColumn column in schemaColumns)
{
mapColumns.Add(string.Format("C{0}", column.Ordinal), Table.Columns[column.ColumnName].Ordinal);
}
if (forSynchValues)
{
LoadXMLOnlyForSync(reader, _OptimumSize, mapColumns);
}
else
{
LoadXMLAllRows(reader, _OptimumSize, mapColumns);
}
reader.ReadEndElement();
}
///
/// Determines whether [is zero or empty] [the specified value].
///
/// The value.
///
/// true if [is zero or empty] [the specified value]; otherwise, false.
///
protected bool IsZeroOrEmpty(object value)
{
Type type = value.GetType();
if (type == typeof(string))
{
return (string)value == string.Empty;
}
else
{
if (type.IsNumericType())
{
return value.Equals(0);
}
}
return false;
}
///
/// Gets the zero or empty value.
///
/// The type.
///
protected object GetZeroOrEmptyValue(Type type)
{
if (type == typeof(string))
{
return string.Empty;
}
else
{
if (type.IsNumericType())
{
return 0;
}
}
return string.Empty;
}
///
/// Creates the XML only for sync values.
///
/// The writer.
private void CreateXMLOnlyForSync(System.Xml.XmlWriter writer)
{
string[] exceptColNames = _ExceptColumnNames.SplitCommaString(trimSpace: true);
string[] colNames = _ColumnNames.SplitCommaString(trimSpace: true);
string[] keyFields = _KeyFields.SplitCommaString(trimSpace: true);
Dictionary columns = new Dictionary();
Dictionary modifiedColumns = new Dictionary();
Dictionary primaryKeysColumns = new Dictionary();
foreach (DataColumn column in Table.Columns)
{
if (string.IsNullOrEmpty(column.Expression) && !column.ReadOnly && (colNames.Length == 0 || Array.IndexOf(colNames, column.ColumnName) > -1)
&& (exceptColNames.Length == 0 || Array.IndexOf(exceptColNames, column.ColumnName) == -1))
{
string colName = string.Format("C{0}", column.Ordinal);
columns.Add(column.Ordinal, colName);
}
}
foreach (string columnName in keyFields)
{
int colNo = Table.Columns[columnName].Ordinal;
string colName = string.Format("C{0}", Table.Columns[columnName].Ordinal);
primaryKeysColumns.Add(colNo, colName);
modifiedColumns.Add(colNo, colName);
}
DataView dataView = new DataView(Table, "", "", DataViewRowState.Added | DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent);
DataRow entity;
foreach (DataRowView rowView in dataView)
{
entity = rowView.Row;
writer.WriteStartElement("R");
writer.WriteAttributeString("rowStated", entity.RowState.ToString());
string colNuls = string.Empty;
string colZeroOrEmpty = string.Empty;
switch (entity.RowState)
{
case DataRowState.Added:
foreach (KeyValuePair column in columns)
{
if (!entity.IsNull(column.Key))
{
if (_OptimumSize && IsZeroOrEmpty(entity[column.Key]))
{
colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString();
}
else
{
writer.WriteElementString(column.Value, entity[column.Key].StringFrom());
}
}
else
{
colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString();
}
}
break;
case DataRowState.Deleted:
foreach (KeyValuePair column in primaryKeysColumns)
{
writer.WriteElementString(column.Value, entity[column.Key, DataRowVersion.Original].StringFrom());
}
break;
case DataRowState.Modified:
//Saves only columns has value change
if (!entity.IsChanged())
{
continue;
}
foreach (KeyValuePair column in columns)
{
if (entity.IsChanged(column.Key))
{
if (!entity.IsNull(column.Key))
{
if (_OptimumSize && IsZeroOrEmpty(entity[column.Key]))
{
colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString();
}
else
{
writer.WriteElementString(column.Value, entity[column.Key].StringFrom());
}
}
else
{
colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString();
}
}
}
break;
}
bool isNull = !string.IsNullOrWhiteSpace(colNuls);
bool isZeroOrEmpty = !string.IsNullOrWhiteSpace(colZeroOrEmpty);
if (isNull || isZeroOrEmpty)
{
writer.WriteStartElement("CS");
if (isNull) writer.WriteAttributeString("N", colNuls);
if (isZeroOrEmpty) writer.WriteAttributeString("E", colZeroOrEmpty);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
///
/// Creates the XML all rows.
///
/// The writer.
private void CreateXMLAllRows(System.Xml.XmlWriter writer)
{
string[] exceptColNames = _ExceptColumnNames.SplitCommaString(trimSpace: true);
string[] colNames = _ColumnNames.SplitCommaString(trimSpace: true);
DataView dataView = new DataView(Table, "", "", DataViewRowState.CurrentRows);
DataRow entity;
Dictionary columns = new Dictionary();
foreach (DataColumn column in Table.Columns)
{
if (string.IsNullOrEmpty(column.Expression) && !column.ReadOnly && (colNames.Length == 0 || Array.IndexOf(colNames, column.ColumnName) > -1)
&& (exceptColNames.Length == 0 || Array.IndexOf(exceptColNames, column.ColumnName) == -1))
{
columns.Add(column.Ordinal, string.Format("C{0}", column.Ordinal));
}
}
foreach (DataRowView rowView in dataView)
{
entity = rowView.Row;
writer.WriteStartElement("R");
string colNuls = string.Empty;
string colZeroOrEmpty = string.Empty;
foreach (KeyValuePair column in columns)
{
if (!entity.IsNull(column.Key))
{
if (_OptimumSize && IsZeroOrEmpty(entity[column.Key]))
{
colZeroOrEmpty += !string.IsNullOrWhiteSpace(colZeroOrEmpty) ? "," + column.Key.ToString() : column.Key.ToString();
}
else
{
writer.WriteElementString(column.Value, entity[column.Key].StringFrom());
}
}
else
{
colNuls += !string.IsNullOrWhiteSpace(colNuls) ? "," + column.Key.ToString() : column.Key.ToString();
}
}
bool isNull = !string.IsNullOrWhiteSpace(colNuls);
bool isZeroOrEmpty = !string.IsNullOrWhiteSpace(colZeroOrEmpty);
if (isNull || isZeroOrEmpty)
{
writer.WriteStartElement("CS");
if (isNull) writer.WriteAttributeString("N", colNuls);
if (isZeroOrEmpty) writer.WriteAttributeString("E", colZeroOrEmpty);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
///
/// Converts an object into its XML representation.
///
/// The stream to which the object is serialized.
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteStartElement("Option");
writer.WriteAttributeString("name", Table.TableName);
writer.WriteAttributeString("includeSchema", (_ProxySchema != DataTableSerializerSchema.None).ToString());
writer.WriteAttributeString("type", _ProxySchema != DataTableSerializerSchema.None ? string.Empty : Table.GetType().AssemblyQualifiedName);
writer.WriteAttributeString("forSynchValues", _ForSynchValues.ToString());
writer.WriteAttributeString("optimumSize", _OptimumSize.ToString());
string extendedProperites = string.Empty;
int p = 0;
foreach (DictionaryEntry prop in Table.ExtendedProperties)
{
extendedProperites += (!string.IsNullOrWhiteSpace(extendedProperites) ? "," : string.Empty) + prop.Key + "|" + prop.Value.GetType().FullName;
writer.WriteAttributeString("extP_" + p++.ToString(), prop.Value.StringFrom());
}
writer.WriteAttributeString("extendedProperties", extendedProperites);
writer.WriteEndElement();
if (_ProxySchema != DataTableSerializerSchema.None)
{
writer.WriteStartElement("Schema");
if (_ProxySchema == DataTableSerializerSchema.DataTableSchema)
{
Table.WriteXmlSchema(writer);
}
if (_ProxySchema == DataTableSerializerSchema.ColumnSchema)
{
writer.WriteAttributeString("type", _ProxySchema.ToString());
foreach (DataColumn col in Table.Columns)
{
writer.WriteStartElement(col.ColumnName);
writer.WriteAttributeString("type", col.DataType.FullName);
writer.WriteAttributeString("defaultValue", col.DefaultValue == DBNull.Value ? "" : col.DefaultValue.ToString());
writer.WriteEndElement();
}
}
writer.WriteEndElement();
}
if (_ForSynchValues)
{
CreateXMLOnlyForSync(writer);
}
else
{
CreateXMLAllRows(writer);
}
}
#endregion
}
}