DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
BinarySerializer.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Linq;
21 using System.Reflection;
22 using System.Text;
23 
24 namespace Deveel.Data.Serialization {
25  public sealed class BinarySerializer {
26  public BinarySerializer() {
27  Encoding = Encoding.Unicode;
28  }
29 
30  public Encoding Encoding { get; set; }
31 
32  public object Deserialize(Stream stream, Type graphType) {
33  if (stream == null)
34  throw new ArgumentNullException("stream");
35  if (!stream.CanRead)
36  throw new ArgumentException("The input stream cannot be read.", "stream");
37 
38  var reader = new BinaryReader(stream, Encoding);
39  return Deserialize(reader, graphType);
40  }
41 
42  public object Deserialize(BinaryReader reader, Type graphType) {
43  if (reader == null)
44  throw new ArgumentNullException("reader");
45  if (graphType == null)
46  throw new ArgumentNullException("graphType");
47 
48  if (!Attribute.IsDefined(graphType, typeof (SerializableAttribute)))
49  throw new ArgumentException(String.Format("The type '{0}' is not marked as serializable.", graphType));
50 
51  if (typeof (ISerializable).IsAssignableFrom(graphType))
52  return CustomDeserialize(reader, graphType);
53 
54  return DeserializeType(reader, graphType);
55  }
56 
57  private object DeserializeType(BinaryReader reader, Type graphType) {
58  var ctor = GetDefaultConstructor(graphType);
59  if (ctor == null)
60  throw new NotSupportedException(String.Format("The type '{0}' does not specify any default empty constructor.", graphType));
61 
62  var fields = graphType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
63  .Where(member => !member.IsDefined(typeof (NonSerializedAttribute), false));
64  var properties = graphType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
65  .Where(member => member.CanWrite && !member.IsDefined(typeof (NonSerializedAttribute), false));
66 
67  var members = new List<MemberInfo>();
68  members.AddRange(fields.Cast<MemberInfo>());
69  members.AddRange(properties.Cast<MemberInfo>());
70 
71  var values = new Dictionary<string, object>();
72  ReadValues(reader, Encoding, values);
73 
74  var obj = ctor.Invoke(new object[0]);
75 
76  foreach (var member in members) {
77  var memberName = member.Name;
78  object value;
79 
80  if (values.TryGetValue(memberName, out value)) {
81  // TODO: convert the source value to the destination value...
82 
83  if (member is PropertyInfo) {
84  var property = (PropertyInfo) member;
85  property.SetValue(obj, value, null);
86  } else if (member is FieldInfo) {
87  var field = (FieldInfo) member;
88  field.SetValue(obj, value);
89  }
90  }
91  }
92 
93  return obj;
94  }
95 
96  private ConstructorInfo GetDefaultConstructor(Type type) {
97  var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
98  foreach (var ctor in ctors) {
99  if (ctor.GetParameters().Length == 0)
100  return ctor;
101  }
102 
103  return null;
104  }
105 
106  private object CustomDeserialize(BinaryReader reader, Type graphType) {
107  var ctor = GetSpecialConstructor(graphType);
108  if (ctor == null)
109  throw new NotSupportedException(String.Format("The type '{0}' has not the special serialization constructor",
110  graphType));
111 
112  var values = new Dictionary<string, object>();
113  ReadValues(reader, Encoding, values);
114 
115  var graph = new ObjectData(graphType, values);
116  return ctor.Invoke(new object[] {graph});
117  }
118 
119  private static void ReadValues(BinaryReader reader, Encoding encoding, IDictionary<string, object> values) {
120  int count = reader.ReadInt32();
121 
122  for (int i = 0; i < count; i++) {
123  var keyLen = reader.ReadInt32();
124  var keyChars = reader.ReadChars(keyLen);
125  var key = new string(keyChars);
126 
127  var value = ReadValue(reader, encoding);
128 
129  values[key] = value;
130  }
131  }
132 
133  private static object ReadValue(BinaryReader reader, Encoding encoding) {
134  var typeCode = reader.ReadByte();
135  var nullCheck = reader.ReadBoolean();
136 
137  if (nullCheck)
138  return null;
139 
140  if (typeCode == BooleanType)
141  return reader.ReadBoolean();
142  if (typeCode == ByteType)
143  return reader.ReadByte();
144  if (typeCode == Int16Type)
145  return reader.ReadInt16();
146  if (typeCode == Int32Type)
147  return reader.ReadInt32();
148  if (typeCode == Int64Type)
149  return reader.ReadInt64();
150  if (typeCode == SingleType)
151  return reader.ReadSingle();
152  if (typeCode == DoubleType)
153  return reader.ReadDouble();
154  if (typeCode == StringType)
155  return reader.ReadString();
156  if (typeCode == ObjectType)
157  return ReadObject(reader, encoding);
158  if (typeCode == ArrayType)
159  return ReadArray(reader, encoding);
160 
161  throw new NotSupportedException("Invalid type code in serialization graph");
162  }
163 
164  private static Type ReadType(BinaryReader reader) {
165  var typeString = reader.ReadString();
166  return Type.GetType(typeString, true);
167  }
168 
169  private static object ReadObject(BinaryReader reader, Encoding encoding) {
170  var objType = ReadType(reader);
171  var serializer = new BinarySerializer {
172  Encoding = encoding
173  };
174 
175  return serializer.Deserialize(reader, objType);
176  }
177 
178  private static Array ReadArray(BinaryReader reader, Encoding encoding) {
179  var objType = ReadType(reader);
180  var arrayLength = reader.ReadInt32();
181  var array = Array.CreateInstance(objType, arrayLength);
182  for (int i = 0; i < arrayLength; i++) {
183  array.SetValue(ReadValue(reader, encoding), i);
184  }
185 
186  return array;
187  }
188 
189  private ConstructorInfo GetSpecialConstructor(Type type) {
190  var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
191  foreach (var ctor in ctors) {
192  var paramTypes = ctor.GetParameters().Select(x => x.ParameterType).ToArray();
193  if (paramTypes.Length == 1 && paramTypes[0] == typeof(ObjectData))
194  return ctor;
195  }
196 
197  return null;
198  }
199 
200  public void Serialize(Stream stream, object obj) {
201  if (stream == null)
202  throw new ArgumentNullException("stream");
203 
204  if (!stream.CanWrite)
205  throw new ArgumentException("The serialization stream is not writeable.");
206 
207  var writer = new BinaryWriter(stream, Encoding);
208  Serialize(writer, obj);
209  }
210 
211  public void Serialize(BinaryWriter writer, object obj) {
212  if (writer == null)
213  throw new ArgumentNullException("writer");
214  if (obj == null)
215  throw new ArgumentNullException("obj");
216 
217  var objType = obj.GetType();
218 
219  if (!Attribute.IsDefined(objType, typeof(SerializableAttribute)))
220  throw new ArgumentException(String.Format("The type '{0} is not serializable", objType.FullName));
221 
222  var graph = new SerializeData(objType);
223 
224  if (typeof (ISerializable).IsAssignableFrom(objType)) {
225  ((ISerializable) obj).GetData(graph);
226  } else {
227  GetObjectValues(objType, obj, graph);
228  }
229 
230  SerializeGraph(writer, Encoding, graph);
231  }
232 
233  private static void GetObjectValues(Type objType, object obj, SerializeData graph) {
234  var fields = objType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
235  .Where(x => !x.IsDefined(typeof (NonSerializedAttribute), false) && !x.Name.EndsWith("_BackingField"));
236  var properties = objType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
237  .Where(x => !x.IsDefined(typeof (NonSerializedAttribute), false) && x.CanRead);
238 
239  var members = new List<MemberInfo>();
240  members.AddRange(fields.Cast<MemberInfo>());
241  members.AddRange(properties.Cast<MemberInfo>());
242 
243  foreach (var member in members) {
244  var memberName = member.Name;
245  Type memberType;
246 
247  object value;
248  if (member is FieldInfo) {
249  value = ((FieldInfo) member).GetValue(obj);
250  memberType = ((FieldInfo) member).FieldType;
251  } else if (member is PropertyInfo) {
252  value = ((PropertyInfo) member).GetValue(obj, null);
253  memberType = ((PropertyInfo) member).PropertyType;
254  } else {
255  throw new NotSupportedException();
256  }
257 
258  graph.SetValue(memberName, memberType, value);
259  }
260  }
261 
262  private static void SerializeGraph(BinaryWriter writer, Encoding encoding, SerializeData graph) {
263  var values = graph.Values.ToDictionary(x => x.Key, x => x.Value);
264  var count = values.Count;
265 
266  writer.Write(count);
267 
268  foreach (var pair in values) {
269  var key = pair.Key;
270  var keyLength = key.Length;
271 
272  writer.Write(keyLength);
273  writer.Write(key.ToCharArray());
274 
275  SerializeValue(writer, encoding, pair.Value.Key, pair.Value.Value);
276  }
277  }
278 
279  private const byte BooleanType = 1;
280  private const byte ByteType = 2;
281  private const byte Int16Type = 3;
282  private const byte Int32Type = 4;
283  private const byte Int64Type = 5;
284  private const byte SingleType = 6;
285  private const byte DoubleType = 7;
286  private const byte StringType = 8;
287  private const byte ObjectType = 15;
288  private const byte ArrayType = 20;
289 
290  private static byte? GetTypeCode(Type type) {
291  if (type.IsArray)
292  return ArrayType;
293 
294  if (type.IsPrimitive) {
295  if (type == typeof(bool))
296  return BooleanType;
297  if (type == typeof(byte))
298  return ByteType;
299  if (type == typeof(short))
300  return Int16Type;
301  if (type == typeof(int))
302  return Int32Type;
303  if (type == typeof(long))
304  return Int64Type;
305  if (type == typeof(float))
306  return SingleType;
307  if (type == typeof(double))
308  return DoubleType;
309  }
310 
311  if (type == typeof (string))
312  return StringType;
313 
314  if (Attribute.IsDefined(type, typeof(SerializableAttribute)))
315  return ObjectType;
316 
317  return null;
318  }
319 
320  private static void SerializeValue(BinaryWriter writer, Encoding encoding, Type type, object value) {
321  var typeCode = GetTypeCode(type);
322  if (typeCode == null)
323  throw new NotSupportedException(String.Format("The type '{0}' is not supported.", type));
324 
325  var nullCheck = value == null;
326 
327  writer.Write(typeCode.Value);
328  writer.Write(nullCheck);
329 
330  if (value == null)
331  return;
332 
333  if (typeCode == ArrayType) {
334  var typeString = type.GetElementType().FullName;
335  writer.Write(typeString);
336 
337  var array = (Array) value;
338  var arrayLength = array.Length;
339  var arrayType = type.GetElementType();
340 
341  writer.Write(arrayLength);
342 
343  for (int i = 0; i < arrayLength; i++) {
344  var element = array.GetValue(i);
345  SerializeValue(writer, encoding, arrayType, element);
346  }
347  } else if (typeCode == ObjectType) {
348  var realType = value.GetType();
349  writer.Write(realType.FullName);
350 
351  var serializer = new BinarySerializer {Encoding = encoding};
352  serializer.Serialize(writer, value);
353  } else if (typeCode == BooleanType) {
354  writer.Write((bool) value);
355  } else if (typeCode == ByteType) {
356  writer.Write((byte) value);
357  } else if (typeCode == Int16Type) {
358  writer.Write((short) value);
359  } else if (typeCode == Int32Type) {
360  writer.Write((int) value);
361  } else if (typeCode == Int64Type) {
362  writer.Write((long) value);
363  } else if (typeCode == SingleType) {
364  writer.Write((float) value);
365  } else if (typeCode == DoubleType) {
366  writer.Write((double) value);
367  } else if (typeCode == StringType) {
368  writer.Write((string) value);
369  }
370  }
371  }
372 }
IEnumerable< KeyValuePair< string, KeyValuePair< Type, object > > > Values
static void ReadValues(BinaryReader reader, Encoding encoding, IDictionary< string, object > values)
static Array ReadArray(BinaryReader reader, Encoding encoding)
object Deserialize(Stream stream, Type graphType)
void Serialize(Stream stream, object obj)
object CustomDeserialize(BinaryReader reader, Type graphType)
ConstructorInfo GetDefaultConstructor(Type type)
ConstructorInfo GetSpecialConstructor(Type type)
static void SerializeValue(BinaryWriter writer, Encoding encoding, Type type, object value)
void Serialize(BinaryWriter writer, object obj)
static object ReadObject(BinaryReader reader, Encoding encoding)
object Deserialize(BinaryReader reader, Type graphType)
static Type ReadType(BinaryReader reader)
static object ReadValue(BinaryReader reader, Encoding encoding)
static void GetObjectValues(Type objType, object obj, SerializeData graph)
object DeserializeType(BinaryReader reader, Type graphType)
static void SerializeGraph(BinaryWriter writer, Encoding encoding, SerializeData graph)