sharplib/ser/XmlFormatter2.cs

767 lines
17 KiB
C#

using System;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
//using System.Web.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace lib
{
public class AttDocumentSubclasses : Attribute
{
}
public interface I_Serialize
{
void OnSerialize();
void OnDeserialize( object enclosing );
}
public enum Datastructure
{
Invalid,
Tree,
Full,
}
public class XmlFormatter2Cfg: Config
{
public readonly Datastructure datastructure = Datastructure.Full;
}
public class XmlFormatter2: IFormatter
{
public StreamingContext Context { get; set; }
static Random s_rnd = new Random();
int m_rndVal = s_rnd.Next();
XmlFormatter2Cfg m_cfg = new XmlFormatter2Cfg();
#region Unimplimented
public ISurrogateSelector SurrogateSelector
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public SerializationBinder Binder
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
#endregion
public XmlFormatter2()
{
Context = new StreamingContext( StreamingContextStates.All );
}
public XmlFormatter2( XmlFormatter2Cfg cfg )
{
Context = new StreamingContext( StreamingContextStates.All );
m_cfg = cfg;
}
#region Deserialize
private static FormatterConverter s_conv = new FormatterConverter();
public object Deserialize( Stream stream )
{
//lib.log.info( "Deserialize( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return DeserializeKnownType( stream, null );
//lib.log.info( "Deserialize END ( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
public T Deserialize<T>(Stream stream)
{
//lib.log.info( "Deserialize( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return (T)DeserializeKnownType(stream, typeof(T));
//lib.log.info( "Deserialize END ( Stream stream ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
public object DeserializeKnownType( Stream stream, Type t )
{
//lib.log.info( "DeserializeKnownType( Stream stream, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
XmlTextReader reader = new XmlTextReader(stream);
//reader.Settings = System.Text.Encoding.ASCII;
object obj = Deserialize(reader, t);
//lib.log.info( "DeserializeKnownType END( Stream stream, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
return obj;
}
private object Deserialize( XmlReader reader, Type t )
{
//lib.log.info( "Deserialize( XmlReader reader, Type t ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
m_alreadySerialized.Clear();
m_objectID = new ObjectIDGenerator();
reader.Read();
XmlDocument doc = new XmlDocument();
doc.Load( reader );
////lib.log.info( "What to deserialize {0}", doc.OuterXml.ToString() );
if( t == null )
return Deserialize( doc.DocumentElement );
return Deserialize( doc.DocumentElement, t );
}
private object Deserialize( XmlElement elem )
{
//lib.log.info( "object Deserialize( XmlElement elem ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
string typename = elem.HasAttribute("t") ? elem.GetAttribute("t") : elem.Name;
return Deserialize( elem, typename );
}
private object Deserialize( XmlElement elem, string typename )
{
AppDomain currentDomain = AppDomain.CurrentDomain;
Assembly[] assems = currentDomain.GetAssemblies();
Type type = null;
// @@@@: This should go backwards, we tend to lookup our own stuff, then builtins.
// Also, cache a typename into its assembly.
foreach( Assembly a in assems )
{
type = a.GetType( typename );
if( type != null )
break;
}
if( type == null )
{
return null;
}
return Deserialize( elem, type );
}
private object Deserialize( XmlElement elem, Type type, object enclosing = null )
{
TypeCode typeCode = Type.GetTypeCode(type);
if( typeCode != TypeCode.Object )
{
return DeserializeConcrete( elem, type );
}
else
{
if( !type.IsArray )
{
object obj = DeserializeObject(elem, type);
if( obj is I_Serialize )
{
var iser = obj as I_Serialize;
iser.OnDeserialize( enclosing );
}
return obj;
}
else
{
return DeserializeArray( elem, type );
}
}
}
Type[] mm_types = new Type[1];
private object GetDefault( Type t )
{
mm_types[0] = t;
var fn = GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(mm_types);
return fn.Invoke( this, null );
}
public T GetDefaultGeneric<T>()
{
return default( T );
}
private object DeserializeConcrete( XmlElement elem, Type type )
{
string val = "";
if( elem.HasAttribute("v") )
{
val = elem.GetAttribute("v");
}
else
{
val = elem.InnerText;
}
if ( !type.IsEnum )
{
try
{
return s_conv.Convert( val, type );
}
catch( Exception )
{
return GetDefault( type );
}
}
else
{
return Enum.Parse( type, val );
}
}
private XmlElement getNamedChild( XmlNodeList list, string name )
{
foreach( XmlNode node in list )
{
if( node.Name == name )
{
return (XmlElement)node;
}
}
return null;
}
private Type FindType( string shortname )
{
Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
foreach( Assembly a in ass )
{
Type t = a.GetType(shortname);
if( t != null )
{
return t;
}
}
return null;
}
private Type[] mm_consType = new Type[2];
private object[] mm_args = new object[2];
private object DeserializeObject( XmlElement elem, Type type )
{
string refString = elem.GetAttribute("ref");
int refInt = refString.Length > 0 ? Convert.ToInt32(refString) : -1;
var finalType = type;
if( elem.HasAttribute( "t" ) )
{
var typename = elem.GetAttribute("t");
finalType = FindType( typename );
if( finalType == null )
finalType = type;
}
object obj = createObject(finalType, refInt);
if( obj is IList )
{
var list = obj as IList;
return DeserializeList( elem, type, list );
}
Type typeISerializable = typeof(ISerializable);
if( obj is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
{
XmlNodeList allChildren = elem.ChildNodes;
//ISerializable ser = obj as ISerializable;
var serInfo = new SerializationInfo(finalType, new FormatterConverter());
//var serInfoForTypes = new SerializationInfo( type, new FormatterConverter() );
//ser.GetObjectData( serInfoForTypes, Context );
foreach( var objNode in allChildren )
{
var node = objNode as XmlElement;
String name = node.Name;
String childType = node.GetAttribute("t");
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild(allChildren, name);
var des = Deserialize(childElem, childType);
serInfo.AddValue( name, des, des.GetType() );
}
//ConstructorInfo[] allCons = obj.GetType().GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
//var serMem = FormatterServices.GetSerializableMembers( finalType );
//object objUn = FormatterServices.GetSafeUninitializedObject( finalType );
IDeserializationCallback objUnOnDeser = obj as IDeserializationCallback;
mm_consType[0] = typeof( SerializationInfo );
mm_consType[1] = typeof( StreamingContext );
ConstructorInfo serCons = finalType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, mm_consType, null);
mm_args[0] = serInfo;
mm_args[1] = Context;
serCons.Invoke( obj, mm_args );
if( objUnOnDeser != null )
{
objUnOnDeser.OnDeserialization( objUnOnDeser );
}
/*
ser.GetObjectData( serInfo, Context );
//var serEnum = ;
foreach( var serMember in serInfo )
{
String name = serMember.Name;
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild( allChildren, name );
var des = Deserialize( childElem, name );
}
*/
}
else
{
XmlNodeList allChildren = elem.ChildNodes;
var fields = refl.GetAllFields(type);
//MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context );
foreach( var childFi in fields )
{
String name = childFi.Name;
name = refl.TypeToIdentifier( name );
XmlElement childElem = getNamedChild(allChildren, name);
if( childElem != null )
{
object childObj = Deserialize(childElem, childFi.FieldType, obj);
childFi.SetValue( obj, childObj );
}
else if( fields.Count == 1 )
{
object childObj = Deserialize(elem, childFi.FieldType, obj);
childFi.SetValue( obj, childObj );
}
}
}
return obj;
}
private object DeserializeList( XmlElement elem, Type type, IList list )
{
XmlNodeList arrNodeList = elem.ChildNodes;
Type t = list.GetType();
Type[] genT = t.GetGenericArguments();
Debug.Assert( genT.Length == 1 );
for( int i = 0; i < arrNodeList.Count; ++i )
{
if( arrNodeList.Item( i ) is XmlElement )
{
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
list.Add( Deserialize( arrElem, genT[0] ) );
}
}
return list;
}
private object DeserializeArray( XmlElement elem, Type type )
{
Type typeElem = type.GetElementType();
string refString = elem.GetAttribute("ref");
int refInt = refString.Length > 0 ? Convert.ToInt32(refString) : -1;
XmlNodeList arrNodeList = elem.ChildNodes;
int length = arrNodeList.Count;
Array arr = createArray(typeElem, refInt, length);
for( int i = 0; i < arr.Length; ++i )
{
if( arrNodeList.Item( i ) is XmlElement )
{
XmlElement arrElem = (XmlElement)arrNodeList.Item(i);
var finalType = typeElem;
if (arrElem.HasAttribute("t"))
{
var typename = arrElem.GetAttribute("t");
finalType = FindType(typename);
if (finalType == null)
finalType = typeElem;
}
arr.SetValue( Deserialize( arrElem, finalType), i );
}
}
return arr;
}
private object createObject( string typename, int refInt )
{
Type type = Type.GetType(typename);
return createObject( type, refInt );
}
private object createObject( Type type, int refInt )
{
TypeCode tc = Type.GetTypeCode(type);
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
{
//lib.log.info( "Reusing object for {0}", refInt );
return m_alreadySerialized[refInt];
}
else
{
object obj = null;
try
{
//Trying the nice way to creat objects first.
obj = Activator.CreateInstance( type );
}
catch( Exception )
{
try
{
obj = System.Runtime.Serialization.FormatterServices.GetUninitializedObject( type );
}
catch( Exception exInner )
{
log.error( $"Got exception {exInner.Message} trying to make an uninitialized object" );
}
}
if( obj == null )
{
log.warn( $"Could not create object of type {type.Name}" );
return obj;
}
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 )
{
m_alreadySerialized[refInt] = obj;
}
return obj;
}
}
private Array createArray( string elemTypename, int refInt, int length )
{
Type elemType = Type.GetType(elemTypename);
return createArray( elemType, refInt, length );
}
private Array createArray( Type elemType, int refInt, int length )
{
TypeCode elemTC = Type.GetTypeCode(elemType);
if( m_cfg.datastructure == Datastructure.Full && refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) )
{
return (Array)m_alreadySerialized[refInt];
}
else
{
Array arr = Array.CreateInstance(elemType, length);
if( m_cfg.datastructure == Datastructure.Full )
{
m_alreadySerialized[refInt] = arr;
}
return arr;
}
}
private ObjectIDGenerator m_objectID = new ObjectIDGenerator();
private Dictionary<long, object> m_alreadySerialized = new Dictionary<long, object>();
#endregion
#region Serialize
private string getTypeName( Type type )
{
//Assembly ass = type.Assembly;
//string assName = ass.GetName().Name;
return type.FullName; // + ", " + assName;
}
public void Serialize( Stream stream, object root )
{
//lib.log.info( "Serialize( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
m_alreadySerialized.Clear();
m_objectID = new ObjectIDGenerator();
XmlTextWriter writer = new XmlTextWriter(stream, System.Text.Encoding.ASCII);
writer.Formatting = Formatting.Indented;
Serialize( writer, root );
//Rely on the parent closing the stream.
//writer.Close();
writer.Flush();
//lib.log.info( "Serialize END ( Stream stream, object root ) {0} {1}", m_rndVal, m_alreadySerialized.Count );
}
private void Serialize( XmlWriter writer, object root )
{
//writer.WriteStartDocument();
Serialize( writer, root, "root", true );
//writer.WriteEndDocument();
}
private void Serialize( XmlWriter writer, object root, string name, bool forceType )
{
writer.WriteStartElement( name );
if( root != null )
{
Type type = root.GetType();
TypeCode typeCode = Type.GetTypeCode(type);
if( typeCode != TypeCode.Object )
{
SerializeConcrete( writer, root, forceType );
}
else
{
if( !type.IsArray )
{
SerializeObject( writer, root );
}
else
{
SerializeArray( writer, root );
}
}
}
else
{
writer.WriteAttributeString( "v", "null" );
}
writer.WriteEndElement();
}
private void SerializeConcrete( XmlWriter writer, object root, bool forceType )
{
//TODO: Only write this out if debugging.
if( forceType )
{
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) );
}
writer.WriteAttributeString( "v", root.ToString() );
}
private void SerializeObject( XmlWriter writer, object root )
{
writer.WriteAttributeString( "t", getTypeName( root.GetType() ) );
/*
if( root is IList )
{
var list = root as IList;
Type t = root.GetType();
Type[] genT = t.GetGenericArguments();
}
*/
bool first;
long refInt = m_objectID.GetId(root, out first);
if( m_cfg.datastructure == Datastructure.Full )
{
writer.WriteAttributeString( "ref", refInt.ToString() );
}
if( first )
{
if( m_cfg.datastructure == Datastructure.Full )
{
m_alreadySerialized[refInt] = root;
}
Type type = root.GetType();
//*
Type typeISerializable = typeof(ISerializable);
if( root is ISerializable ) // type.IsSubclassOf( typeISerializable ) )
{
ISerializable ser = root as ISerializable;
var serInfo = new SerializationInfo(type, new FormatterConverter());
ser.GetObjectData( serInfo, Context );
//var serEnum = ;
foreach( var serMember in serInfo )
{
String name = serMember.Name;
name = refl.TypeToIdentifier( name );
Serialize( writer, serMember.Value, name, true );
}
//var sc = new SerializationContext(
//ser.GetObjectData(
}
else
//*/
{
var fields = refl.GetAllFields(type);
//MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context );
foreach( var childFi in fields )
{
object[] objs = childFi.GetCustomAttributes(typeof(NonSerializedAttribute), true);
if( objs.Length > 0 )
{
continue;
}
String name = childFi.Name;
name = refl.TypeToIdentifier( name );
Serialize( writer, childFi.GetValue( root ), name, false );
}
}
}
}
private void SerializeArray( XmlWriter writer, object root )
{
Array arr = (Array)root;
Type typeElem = arr.GetType().GetElementType();
Type type = root.GetType();
writer.WriteAttributeString( "t", getTypeName( type ) );
bool first;
long refInt = m_objectID.GetId(root, out first);
if( m_cfg.datastructure == Datastructure.Full )
{
writer.WriteAttributeString( "ref", refInt.ToString() );
}
if( first )
{
if( m_cfg.datastructure == Datastructure.Full )
{
m_alreadySerialized[refInt] = root;
}
for( int i = 0; i < arr.Length; ++i )
{
Serialize( writer, arr.GetValue( i ), "i" + i.ToString(), false );
}
}
}
#endregion
}
}