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 interface I_Serialize { void OnSerialize(); void OnDeserialize( object enclosing ); } public class XmlFormatter2 : IFormatter { public StreamingContext Context { get; set; } static private Random s_rnd = new Random(); private int m_rndVal = s_rnd.Next(); #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 ); } #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 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() { return default( T ); } private object DeserializeConcrete( XmlElement elem, Type type ) { string val = elem.GetAttribute( "v" ); 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 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( type, new FormatterConverter() ); ser.GetObjectData( serInfo, Context ); //var serEnum = ; foreach( var serMember in serInfo ) { String name = serMember.Name; name = name.Replace( '+', '-' ); name = name.Replace( '`', '_' ); XmlElement childElem = getNamedChild( allChildren, name ); var des = Deserialize( childElem, name ); } } else */ { MemberInfo[] miArr = FormatterServices.GetSerializableMembers( finalType ); XmlNodeList allChildren = elem.ChildNodes; for( int i = 0; i < miArr.Length; ++i ) { FieldInfo childFi = (FieldInfo)miArr[ i ]; String name = childFi.Name; name = name.Replace( '+', '-' ); name = name.Replace( '`', '_' ); XmlElement childElem = getNamedChild( allChildren, name ); if( childElem != null ) { object childObj = Deserialize( childElem, 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 ); arr.SetValue( Deserialize( arrElem, typeElem ), 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( refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) ) { //lib.log.info( "Reusing object for {0}", refInt ); return m_alreadySerialized[ refInt ]; } else { //lib.log.info( "Creating new object for {0}", refInt ); object obj = Activator.CreateInstance( type ); if( 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( refInt > 0 && m_alreadySerialized.ContainsKey( refInt ) ) { return (Array)m_alreadySerialized[ refInt ]; } else { Array arr = Array.CreateInstance( elemType, length ) ; m_alreadySerialized[ refInt ] = arr; return arr; } } private ObjectIDGenerator m_objectID = new ObjectIDGenerator(); private Dictionary m_alreadySerialized = new Dictionary(); #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" ); //writer.WriteEndDocument(); } private void Serialize( XmlWriter writer, object root, string name ) { writer.WriteStartElement( name ); if( root != null ) { Type type = root.GetType(); TypeCode typeCode = Type.GetTypeCode( type ); if( typeCode != TypeCode.Object ) { SerializeConcrete( writer, root ); } 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 ) { //TODO: Only write this out if debugging. //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 ); writer.WriteAttributeString( "ref", refInt.ToString() ); if( first ) { 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 = name.Replace( '+', '-' ); name = name.Replace( '`', '_' ); Serialize( writer, serMember.Value, name ); } //var sc = new SerializationContext( //ser.GetObjectData( } else //*/ { MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type, Context ); for( int i = 0; i < miArr.Length; ++i ) { FieldInfo childFi = (FieldInfo)miArr[ i ]; object[] objs = childFi.GetCustomAttributes( typeof( NonSerializedAttribute ), true ); if( objs.Length > 0 ) { continue; } String name = childFi.Name; name = name.Replace( '+', '-' ); name = name.Replace( '`', '_' ); Serialize( writer, childFi.GetValue( root ), name ); } } } } private void SerializeArray( XmlWriter writer, object root ) { Array arr = (Array)root; Type typeElem = arr.GetType().GetElementType(); writer.WriteAttributeString( "t", getTypeName( typeElem ) ); bool first; long refInt = m_objectID.GetId( root, out first ); writer.WriteAttributeString( "ref", refInt.ToString() ); if( first ) { m_alreadySerialized[ refInt ] = root; for( int i = 0; i < arr.Length; ++i ) { Serialize( writer, arr.GetValue( i ), "i" + i.ToString() ); } } } #endregion } }