CefValueSerialization.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Dynamic;
  5. using System.Linq;
  6. namespace Xilium.CefGlue.Common.Shared.Serialization
  7. {
  8. internal static class CefValueSerialization
  9. {
  10. public enum BinaryMagicBytes : byte
  11. {
  12. DateTime,
  13. Binary
  14. }
  15. public static void Serialize(object value, CefValueWrapper cefValue)
  16. {
  17. Serialize(value, new Stack<object>(), cefValue);
  18. }
  19. private static void Serialize(object value, Stack<object> visitedObjects, CefValueWrapper cefValue)
  20. {
  21. if (value == null)
  22. {
  23. cefValue.SetNull();
  24. return;
  25. }
  26. if (value is byte[] byteArr)
  27. {
  28. // handle binaries in a special way (otherwise it will fall on object and be serialized as a collection)
  29. cefValue.SetBinary(ToCefBinary(BinaryMagicBytes.Binary, byteArr));
  30. return;
  31. }
  32. var typeCode = Type.GetTypeCode(value.GetType());
  33. switch (typeCode)
  34. {
  35. case TypeCode.Object:
  36. if (visitedObjects.Any(o => o == value))
  37. {
  38. throw new InvalidOperationException("Cycle found in result");
  39. }
  40. visitedObjects.Push(value);
  41. SerializeComplexObject(value, visitedObjects, cefValue);
  42. visitedObjects.Pop();
  43. break;
  44. case TypeCode.Boolean:
  45. cefValue.SetBool((bool)value);
  46. break;
  47. case TypeCode.Byte:
  48. cefValue.SetInt((byte)value);
  49. break;
  50. case TypeCode.Char:
  51. cefValue.SetString(((char)value).ToString());
  52. break;
  53. case TypeCode.DateTime:
  54. // datetime is serialized into a binary (cef value does not support datetime)
  55. var dateBinary = BitConverter.GetBytes(((DateTime)value).Ticks);
  56. cefValue.SetBinary(ToCefBinary(BinaryMagicBytes.DateTime, dateBinary));
  57. break;
  58. case TypeCode.Decimal:
  59. cefValue.SetDouble((double)(decimal)value);
  60. break;
  61. case TypeCode.Double:
  62. cefValue.SetDouble((double)value);
  63. break;
  64. case TypeCode.Single:
  65. cefValue.SetDouble((double)(float)value);
  66. break;
  67. case TypeCode.Empty:
  68. cefValue.SetNull();
  69. break;
  70. case TypeCode.Int16:
  71. cefValue.SetInt((int)(short)value);
  72. break;
  73. case TypeCode.Int32:
  74. cefValue.SetInt((int)value);
  75. break;
  76. case TypeCode.Int64:
  77. cefValue.SetDouble((double)(long)value);
  78. break;
  79. case TypeCode.UInt16:
  80. cefValue.SetInt((int)(ushort)value);
  81. break;
  82. case TypeCode.UInt32:
  83. cefValue.SetInt((int)(uint)value);
  84. break;
  85. case TypeCode.UInt64:
  86. cefValue.SetDouble((double)(ulong)value);
  87. break;
  88. case TypeCode.SByte:
  89. cefValue.SetInt((sbyte)value);
  90. break;
  91. case TypeCode.String:
  92. cefValue.SetString((string)value);
  93. break;
  94. }
  95. }
  96. private static void SerializeComplexObject(object value, Stack<object> visitedObjects, CefValueWrapper cefValue)
  97. {
  98. if (value is IDictionary dictionary)
  99. {
  100. var cefDictionary = ValueServices.CreateDictionary();
  101. foreach (var key in dictionary.Keys)
  102. {
  103. var keyText = key.ToString();
  104. Serialize(dictionary[key], visitedObjects, new CefDictionaryWrapper(cefDictionary, keyText));
  105. }
  106. cefValue.SetDictionary(cefDictionary);
  107. }
  108. else if (value is IEnumerable enumerable)
  109. {
  110. var i = 0;
  111. var cefList = ValueServices.CreateList();
  112. foreach (var item in enumerable)
  113. {
  114. Serialize(item, visitedObjects, new CefListWrapper(cefList, i));
  115. i++;
  116. }
  117. cefValue.SetList(cefList);
  118. }
  119. else
  120. {
  121. var fields = value.GetType().GetFields();
  122. var properties = value.GetType().GetProperties();
  123. var cefDictionary = ValueServices.CreateDictionary();
  124. foreach (var field in fields)
  125. {
  126. Serialize(field.GetValue(value), visitedObjects, new CefDictionaryWrapper(cefDictionary, field.Name));
  127. }
  128. foreach (var property in properties)
  129. {
  130. Serialize(property.GetValue(value), visitedObjects, new CefDictionaryWrapper(cefDictionary, property.Name));
  131. }
  132. cefValue.SetDictionary(cefDictionary);
  133. }
  134. }
  135. public static object DeserializeCefValue(CefValueWrapper cefValue)
  136. {
  137. switch (cefValue.GetValueType())
  138. {
  139. case CefValueType.Binary:
  140. return FromCefBinary(cefValue.GetBinary(), out var kind);
  141. case CefValueType.Bool:
  142. return cefValue.GetBool();
  143. case CefValueType.Dictionary:
  144. IDictionary<string, object> dictionary = new ExpandoObject();
  145. var cefDictionary = cefValue.GetDictionary();
  146. var keys = cefDictionary.GetKeys();
  147. foreach (var key in keys)
  148. {
  149. dictionary[key] = DeserializeCefValue(new CefDictionaryWrapper(cefDictionary, key));
  150. }
  151. return dictionary;
  152. case CefValueType.Double:
  153. return cefValue.GetDouble();
  154. case CefValueType.List:
  155. var cefList = cefValue.GetList();
  156. return DeserializeCefList<object>(cefList);
  157. case CefValueType.Int:
  158. return cefValue.GetInt();
  159. case CefValueType.String:
  160. return cefValue.GetString() ?? ""; // default to "", because cef converts "" to null, and when null it will fall on the Null case
  161. case CefValueType.Null:
  162. return null;
  163. }
  164. return null;
  165. }
  166. public static ListElementType[] DeserializeCefList<ListElementType>(ICefListValue cefList)
  167. {
  168. var array = new ListElementType[cefList.Count];
  169. for (var i = 0; i < cefList.Count; i++)
  170. {
  171. array[i] = (ListElementType)DeserializeCefValue(new CefListWrapper(cefList, i));
  172. }
  173. return array;
  174. }
  175. internal static ICefBinaryValue ToCefBinary(BinaryMagicBytes kind, byte[] originalBinary)
  176. {
  177. var binary = new byte[originalBinary.Length + 1]; // alloc space for the magic byte
  178. binary[0] = (byte)kind;
  179. originalBinary.CopyTo(binary, 1);
  180. return ValueServices.CreateBinary(binary);
  181. }
  182. internal static object FromCefBinary(ICefBinaryValue value, out BinaryMagicBytes kind)
  183. {
  184. var binary = value.ToArray();
  185. if (binary.Length > 0)
  186. {
  187. var rest = binary.Skip(1).ToArray();
  188. kind = (BinaryMagicBytes)binary[0];
  189. switch (kind)
  190. {
  191. case BinaryMagicBytes.Binary:
  192. return rest;
  193. case BinaryMagicBytes.DateTime:
  194. var binaryDate = BitConverter.ToInt64(rest, 0);
  195. return DateTime.FromBinary(binaryDate);
  196. default:
  197. throw new InvalidOperationException("Unrecognized binary type: " + binary[0]);
  198. }
  199. }
  200. kind = BinaryMagicBytes.Binary;
  201. return new byte[0];
  202. }
  203. }
  204. }