4908618aea7c32eb0f94d398b57fa28d

The default XML serializer does some things I consider undesirable
1. The XML it produces is full of unnecessary XML Namespace stuff
2. It is not polymorphic - say you have class B : A { }, and have serialized a type B, you should be able to call Deserialize<A>() on it

My implementation corrects these issues, but it seems a bit crazy!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
	public class XmlStringSerializer
	{
		static Dictionary<string, Type> types = new Dictionary<string, Type>();
		public string Serialize(object o)
		{
			StringBuilder sb = new StringBuilder();
			Type t = o.GetType();
			if (t.IsSerializable)
			{
				XmlSerializer serializer = new XmlSerializer(t);
				using (TextWriter tw = new StringWriter(sb))
				{
					var namespaces = new XmlSerializerNamespaces();
					namespaces.Add("", "");
					serializer.Serialize(tw, o, namespaces);
					tw.Close();
				}
			}
			else
			{
				throw new InvalidOperationException(string.Format("Objects of type {0} are not serializable.", t.Name));
			}
			sb.Remove(0, 42);
			sb.Insert(0, '.');
			sb.Insert(0, t.Namespace);
			sb.Insert(0, '<');
			return sb.ToString();

		}


		public T DeSerializeObject<T>(string content)
		{
			var typeName = content.Substring(1, content.IndexOf(' ', 20) - 1);
			Type type;
			if (!types.ContainsKey(typeName))
			{
				type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Single(t => t.FullName == typeName);
				lock (types)
				{
					types[typeName] = type;
				}
			}
			else
			{
				type = types[typeName];
			}
			object o;
			var serializer = new XmlSerializer(type);
			int posAfterTypeName = content.IndexOf(' ');
			int posOfStartOfTypeName = content.LastIndexOf('.', posAfterTypeName) + 1;

			content = @"<?xml version=""1.0"" encoding=""utf-16""?><" + content.Substring(posOfStartOfTypeName);
			using (TextReader tr = new StringReader(content))
			{
				o = serializer.Deserialize(tr);
				tr.Close();
			}
			return (T) o;
		}
	}

Refactorings

No refactoring yet !

72f36daa501cf8f5bb861210edd9232d

Moonshield

January 22, 2009, January 22, 2009 03:45, permalink

No rating. Login to rate!

Unfortunately, your code wasn't working for me. I think that my default xml encoding differs from yours so that your hard-coded string manipulation isn’t working properly. On the other side, I worked on a cleaner way to achieve what you want. It can serializes an object of type A, then deserializes it back to the same type or to an object of type B : A like you mentioned. The key of the implementation is to use a general root name (see XmlRoot in my code) for both objects. Enough talking, see the code below :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
using System;
using System.IO;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            // Serialize a Person object and deserialize it back to a Person or an Employee
            string SerializedObject = SerializedObject = CustomSerializer.Serialize(new Person() { Id = 1, FirstName = "Peter", LastName = "Pan" });
            Person oPerson = CustomSerializer.DeSerialize<Person>(SerializedObject);
            Employee oEmployee = CustomSerializer.DeSerialize<Employee>(SerializedObject);
            
            // At this point the Employee.Title is empty, everything's normal. Then we will
            // serialize the same Employee object and deserialize it back to a Person object
            oEmployee.Title = "Hero";            
            SerializedObject = CustomSerializer.Serialize(oEmployee);
            oPerson = CustomSerializer.DeSerialize<Person>(SerializedObject);

            Console.ReadLine();
        }
    }

    /// <summary>
    /// Contains information about a person.
    /// </summary>
    /// <remarks>
    /// The XmlRoot attribute allow to serialize a Person object 
    /// and to deserialize it back to a Person or an Employee 
    /// object without "manipulating" the xml string.
    /// </remarks>
    [Serializable, XmlRoot("Base")]
    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    /// <summary>
    /// Contains information about an employee.
    /// </summary>
    /// <remarks>
    /// The XmlRoot attribute allow to serialize a Person object 
    /// and to deserialize it back to a Person or an Employee 
    /// object without "manipulating" the xml string.
    /// </remarks>
    [Serializable, XmlRoot("Base")]
    public class Employee : Person
    {
        public string Title { get; set; }
    }

    /// <summary>
    /// 
    /// </summary>
    public class CustomSerializer
    {
        /// <summary>
        /// Serialize an object to xml
        /// </summary>
        /// <param name="pSource">Object Type</param>
        /// <returns>String</returns>
        public static string Serialize(object pSource)
        {
            // Normally I'd use this 4 lines but since you need cleaner xml see below
            //StringWriter oWriter = new StringWriter();
            //XmlSerializer oSerializer = new XmlSerializer(pSource.GetType());
            //oSerializer.Serialize(oWriter, pSource);
            //return oWriter.ToString();

            XmlSerializerNamespaces oNamespaces = new XmlSerializerNamespaces();
            oNamespaces.Add("", "");

            StringWriter oWriter = new StringWriter();
            XmlSerializer oSerializer = new XmlSerializer(pSource.GetType());
            oSerializer.Serialize(oWriter, pSource, oNamespaces);

            string Serialized = oWriter.ToString();
            return Serialized.Remove(0, Serialized.IndexOf('<', 1));
        }

        /// <summary>
        /// Deserialize an xml string to an object
        /// </summary>
        /// <param name="pXmlSource">Xml Source</param>
        /// <returns>Deserialized object</returns>
        public static T DeSerialize<T>(string pXmlSource)
        {
            return (T)(new XmlSerializer(typeof(T)).Deserialize(new StringReader(pXmlSource)));
        }
    }
}
4908618aea7c32eb0f94d398b57fa28d

mcintyre321

January 22, 2009, January 22, 2009 10:28, permalink

No rating. Login to rate!

Good effort but I'm afraid it doesn't quite fit the bill of what I'm looking for (I should have been a bit clearer about the requirements)! I actually need to have the object B returned as type B, even though it is stored in a field reference of type A. I've rewritten my code so it a/ should work better and b/ has a unit tests. The tests should stay the same

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using NUnit.Framework;

namespace XmlSerializerTests
{
	namespace XmlSerializerTests.SomeOtherNamespace
	{
		[Serializable]
		public class Person
		{
			public int Id { get; set; }
			public string FirstName { get; set; }
			public string LastName { get; set; }

		}
	}
	[Serializable]
	public class Person
	{
		public int Id { get; set; }
		public string FirstName { get; set; }
		public string LastName { get; set; }
		
	}

	[Serializable]
	public class Employee : Person
	{
		public string Title { get; set; }
	}

	[TestFixture]
	public class Tests
	{
		CustomSerializer serializer = new CustomSerializer();
		[Test]
		public void TestThatAClassCanBeDeserializedAsItsBaseClass()
		{
			var employee = new Employee() {FirstName = "John", LastName = "Doe", Id = 1, Title = "Dogsbody"};
			var xml = serializer.Serialize(employee);
			var obj = serializer.DeSerializeObject<Person>(xml);
			Assert.AreEqual(typeof (Employee), obj.GetType());
		}
		[Test, ExpectedException(typeof(InvalidCastException))]
		public void NoDuckSerializationTest()
		{
			var person = new Person() { FirstName = "John", LastName = "Doe", Id = 1};
			var xml = serializer.Serialize(person);
			var someOtherPerson = serializer.DeSerializeObject<XmlSerializerTests.SomeOtherNamespace.Person>(xml);
		}
		
	}

	public class CustomSerializer
	{
		static Dictionary<string, Type> types = new Dictionary<string, Type>();
		public string Serialize(object o)
		{
			StringBuilder sb = new StringBuilder();
			Type t = o.GetType();
			if (t.IsSerializable)
			{
				XmlSerializer serializer = new XmlSerializer(t);
				using (TextWriter tw = new StringWriter(sb))
				{
					var namespaces = new XmlSerializerNamespaces();
					namespaces.Add("", "");
					serializer.Serialize(tw, o, namespaces);
					tw.Close();
				}
			}
			else
			{
				throw new InvalidOperationException(string.Format("Objects of type {0} are not serializable.", t.Name));
			}
			sb.Remove(0, 42);
			sb.Insert(0, '.');
			sb.Insert(0, t.Namespace);
			sb.Insert(0, '<');
			sb.Remove(sb.Length - t.Name.Length - 2, t.Name.Length + 2);
			sb.Append(t.FullName);
			sb.Append(@"/>");
			return sb.ToString();

		}


		public T DeSerializeObject<T>(string content)
		{
			var indexOfFirstSpace = content.IndexOf(' ', 20);
			var indexOfFirstBreak = content.IndexOf("\r\n");
			var indexOfFirstCloseBrace = content.IndexOf(">");
			var endOfTypeNameIndex = new[] {indexOfFirstSpace, indexOfFirstBreak, indexOfFirstCloseBrace}.Where(i => i > -1).Min();

			var typeName = content.Substring(1, endOfTypeNameIndex - 1).Trim();
			Type type;
			if (!types.ContainsKey(typeName))
			{
				type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Single(t => t.FullName == typeName);
				lock (types)
				{
					types[typeName] = type;
				}
			}
			else
			{
				type = types[typeName];
			}
			object o;
			var serializer = new XmlSerializer(type);
			int posAfterTypeName = content.IndexOf(' ');
			int posOfStartOfTypeName = content.LastIndexOf('.', posAfterTypeName) + 1;

			content = @"<?xml version=""1.0"" encoding=""utf-16""?><" + content.Substring(posOfStartOfTypeName);
			content = content.Substring(0, content.Length - typeName.Length - 2) + '/' + type.Name + '>';
			using (TextReader tr = new StringReader(content))
			{
				o = serializer.Deserialize(tr);
				tr.Close();
			}
			return (T)o;
		}

	}
}
F9a9ba6663645458aa8630157ed5e71e

Ants

February 8, 2009, February 08, 2009 16:50, permalink

No rating. Login to rate!

Have you looked at the DataContractSerializer class?

Your refactoring





Format Copy from initial code

or Cancel