PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ServiceStack.Pcl.Mac20/Pcl.Dynamic.cs

http://github.com/ServiceStack/ServiceStack
C# | 188 lines | 155 code | 28 blank | 5 comment | 19 complexity | 8dc3dc3530b419302b025c0e97739679 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. //Copyright (c) Service Stack LLC. All Rights Reserved.
  2. //License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
  3. #if !(PCL || LITE)
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Dynamic;
  7. using ServiceStack.Text;
  8. using ServiceStack.Text.Common;
  9. using ServiceStack.Text.Json;
  10. using System.Linq;
  11. using System.Text;
  12. namespace ServiceStack
  13. {
  14. public static class DeserializeDynamic<TSerializer>
  15. where TSerializer : ITypeSerializer
  16. {
  17. private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>();
  18. private static readonly ParseStringDelegate CachedParseFn;
  19. static DeserializeDynamic()
  20. {
  21. CachedParseFn = ParseDynamic;
  22. }
  23. public static ParseStringDelegate Parse
  24. {
  25. get { return CachedParseFn; }
  26. }
  27. public static IDynamicMetaObjectProvider ParseDynamic(string value)
  28. {
  29. var index = VerifyAndGetStartIndex(value, typeof(ExpandoObject));
  30. var result = new ExpandoObject();
  31. if (JsonTypeSerializer.IsEmptyMap(value)) return result;
  32. var container = (IDictionary<String, Object>)result;
  33. var tryToParsePrimitiveTypes = JsConfig.TryToParsePrimitiveTypeValues;
  34. var valueLength = value.Length;
  35. while (index < valueLength)
  36. {
  37. var keyValue = Serializer.EatMapKey(value, ref index);
  38. Serializer.EatMapKeySeperator(value, ref index);
  39. var elementValue = Serializer.EatValue(value, ref index);
  40. var mapKey = Serializer.UnescapeString(keyValue);
  41. if (JsonUtils.IsJsObject(elementValue))
  42. {
  43. container[mapKey] = ParseDynamic(elementValue);
  44. }
  45. else if (JsonUtils.IsJsArray(elementValue))
  46. {
  47. container[mapKey] = DeserializeList<List<object>, TSerializer>.Parse(elementValue);
  48. }
  49. else if (tryToParsePrimitiveTypes)
  50. {
  51. container[mapKey] = DeserializeType<TSerializer>.ParsePrimitive(elementValue) ?? Serializer.UnescapeString(elementValue);
  52. }
  53. else
  54. {
  55. container[mapKey] = Serializer.UnescapeString(elementValue);
  56. }
  57. Serializer.EatItemSeperatorOrMapEndChar(value, ref index);
  58. }
  59. return result;
  60. }
  61. private static int VerifyAndGetStartIndex(string value, Type createMapType)
  62. {
  63. var index = 0;
  64. if (!Serializer.EatMapStartChar(value, ref index))
  65. {
  66. //Don't throw ex because some KeyValueDataContractDeserializer don't have '{}'
  67. Tracer.Instance.WriteDebug("WARN: Map definitions should start with a '{0}', expecting serialized type '{1}', got string starting with: {2}",
  68. JsWriter.MapStartChar, createMapType != null ? createMapType.Name : "Dictionary<,>", value.Substring(0, value.Length < 50 ? value.Length : 50));
  69. }
  70. return index;
  71. }
  72. }
  73. //TODO: Workout how to fix broken CoreCLR SL5 build that uses dynamic
  74. #if !(SL5 && CORECLR)
  75. public class DynamicJson : DynamicObject
  76. {
  77. private readonly IDictionary<string, object> _hash = new Dictionary<string, object>();
  78. public static string Serialize(dynamic instance)
  79. {
  80. var json = JsonSerializer.SerializeToString(instance);
  81. return json;
  82. }
  83. public static dynamic Deserialize(string json)
  84. {
  85. // Support arbitrary nesting by using JsonObject
  86. var deserialized = JsonSerializer.DeserializeFromString<JsonObject>(json);
  87. var hash = deserialized.ToDictionary<KeyValuePair<string, string>, string, object>(entry => entry.Key, entry => entry.Value);
  88. return new DynamicJson(hash);
  89. }
  90. public DynamicJson(IEnumerable<KeyValuePair<string, object>> hash)
  91. {
  92. _hash.Clear();
  93. foreach (var entry in hash)
  94. {
  95. _hash.Add(Underscored(entry.Key), entry.Value);
  96. }
  97. }
  98. public override bool TrySetMember(SetMemberBinder binder, object value)
  99. {
  100. var name = Underscored(binder.Name);
  101. _hash[name] = value;
  102. return _hash[name] == value;
  103. }
  104. public override bool TryGetMember(GetMemberBinder binder, out object result)
  105. {
  106. var name = Underscored(binder.Name);
  107. return YieldMember(name, out result);
  108. }
  109. public override string ToString()
  110. {
  111. return JsonSerializer.SerializeToString(_hash);
  112. }
  113. private bool YieldMember(string name, out object result)
  114. {
  115. if (_hash.ContainsKey(name))
  116. {
  117. var json = _hash[name].ToString();
  118. if (json.TrimStart(' ').StartsWith("{", StringComparison.Ordinal))
  119. {
  120. result = Deserialize(json);
  121. return true;
  122. }
  123. else if (json.TrimStart(' ').StartsWith("[", StringComparison.Ordinal))
  124. {
  125. result = JsonArrayObjects.Parse(json).Select(a =>
  126. {
  127. var hash = a.ToDictionary<KeyValuePair<string, string>, string, object>(entry => entry.Key, entry => entry.Value);
  128. return new DynamicJson(hash);
  129. }).ToArray();
  130. return true;
  131. }
  132. result = json;
  133. return _hash[name] == result;
  134. }
  135. result = null;
  136. return false;
  137. }
  138. internal static string Underscored(string pascalCase)
  139. {
  140. return Underscored(pascalCase.ToCharArray());
  141. }
  142. internal static string Underscored(IEnumerable<char> pascalCase)
  143. {
  144. var sb = StringBuilderCache.Allocate();
  145. var i = 0;
  146. foreach (var c in pascalCase)
  147. {
  148. if (char.IsUpper(c) && i > 0)
  149. {
  150. sb.Append("_");
  151. }
  152. sb.Append(c);
  153. i++;
  154. }
  155. return StringBuilderCache.ReturnAndFree(sb).ToLowerInvariant();
  156. }
  157. }
  158. #endif
  159. }
  160. #endif