You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
9.9 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Collections.Generic;
  27. using System.Collections.ObjectModel;
  28. using System.Reflection;
  29. using Newtonsoft.Json.Utilities;
  30. using System.Collections;
  31. #if NET20
  32. using Newtonsoft.Json.Utilities.LinqBridge;
  33. #endif
  34. namespace Newtonsoft.Json.Serialization
  35. {
  36. /// <summary>
  37. /// Contract details for a <see cref="System.Type"/> used by the <see cref="JsonSerializer"/>.
  38. /// </summary>
  39. internal class JsonDictionaryContract : JsonContainerContract
  40. {
  41. /// <summary>
  42. /// Gets or sets the dictionary key resolver.
  43. /// </summary>
  44. /// <value>The dictionary key resolver.</value>
  45. public Func<string, string> DictionaryKeyResolver { get; set; }
  46. /// <summary>
  47. /// Gets the <see cref="System.Type"/> of the dictionary keys.
  48. /// </summary>
  49. /// <value>The <see cref="System.Type"/> of the dictionary keys.</value>
  50. public Type DictionaryKeyType { get; }
  51. /// <summary>
  52. /// Gets the <see cref="System.Type"/> of the dictionary values.
  53. /// </summary>
  54. /// <value>The <see cref="System.Type"/> of the dictionary values.</value>
  55. public Type DictionaryValueType { get; }
  56. internal JsonContract KeyContract { get; set; }
  57. private readonly Type _genericCollectionDefinitionType;
  58. private Type _genericWrapperType;
  59. private ObjectConstructor<object> _genericWrapperCreator;
  60. private Func<object> _genericTemporaryDictionaryCreator;
  61. internal bool ShouldCreateWrapper { get; }
  62. private readonly ConstructorInfo _parameterizedConstructor;
  63. private ObjectConstructor<object> _overrideCreator;
  64. private ObjectConstructor<object> _parameterizedCreator;
  65. internal ObjectConstructor<object> ParameterizedCreator
  66. {
  67. get
  68. {
  69. if (_parameterizedCreator == null)
  70. {
  71. _parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(_parameterizedConstructor);
  72. }
  73. return _parameterizedCreator;
  74. }
  75. }
  76. /// <summary>
  77. /// Gets or sets the function used to create the object. When set this function will override <see cref="JsonContract.DefaultCreator"/>.
  78. /// </summary>
  79. /// <value>The function used to create the object.</value>
  80. public ObjectConstructor<object> OverrideCreator
  81. {
  82. get => _overrideCreator;
  83. set => _overrideCreator = value;
  84. }
  85. /// <summary>
  86. /// Gets a value indicating whether the creator has a parameter with the dictionary values.
  87. /// </summary>
  88. /// <value><c>true</c> if the creator has a parameter with the dictionary values; otherwise, <c>false</c>.</value>
  89. public bool HasParameterizedCreator { get; set; }
  90. internal bool HasParameterizedCreatorInternal => (HasParameterizedCreator || _parameterizedCreator != null || _parameterizedConstructor != null);
  91. /// <summary>
  92. /// Initializes a new instance of the <see cref="JsonDictionaryContract"/> class.
  93. /// </summary>
  94. /// <param name="underlyingType">The underlying type for the contract.</param>
  95. public JsonDictionaryContract(Type underlyingType)
  96. : base(underlyingType)
  97. {
  98. ContractType = JsonContractType.Dictionary;
  99. Type keyType;
  100. Type valueType;
  101. if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType))
  102. {
  103. keyType = _genericCollectionDefinitionType.GetGenericArguments()[0];
  104. valueType = _genericCollectionDefinitionType.GetGenericArguments()[1];
  105. if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IDictionary<,>)))
  106. {
  107. CreatedType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
  108. }
  109. else if (underlyingType.IsGenericType())
  110. {
  111. // ConcurrentDictionary<,> + IDictionary setter + null value = error
  112. // wrap to use generic setter
  113. // https://github.com/JamesNK/Newtonsoft.Json/issues/1582
  114. Type typeDefinition = underlyingType.GetGenericTypeDefinition();
  115. if (typeDefinition.FullName == JsonTypeReflector.ConcurrentDictionaryTypeName)
  116. {
  117. ShouldCreateWrapper = true;
  118. }
  119. }
  120. #if NETSTD
  121. IsReadOnlyOrFixedSize = ReflectionUtils.InheritsGenericDefinition(underlyingType, typeof(ReadOnlyDictionary<,>));
  122. #endif
  123. }
  124. #if NETSTD
  125. else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IReadOnlyDictionary<,>), out _genericCollectionDefinitionType))
  126. {
  127. keyType = _genericCollectionDefinitionType.GetGenericArguments()[0];
  128. valueType = _genericCollectionDefinitionType.GetGenericArguments()[1];
  129. if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IReadOnlyDictionary<,>)))
  130. {
  131. CreatedType = typeof(ReadOnlyDictionary<,>).MakeGenericType(keyType, valueType);
  132. }
  133. IsReadOnlyOrFixedSize = true;
  134. }
  135. #endif
  136. else
  137. {
  138. ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType);
  139. if (UnderlyingType == typeof(IDictionary))
  140. {
  141. CreatedType = typeof(Dictionary<object, object>);
  142. }
  143. }
  144. if (keyType != null && valueType != null)
  145. {
  146. _parameterizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(
  147. CreatedType,
  148. typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType),
  149. typeof(IDictionary<,>).MakeGenericType(keyType, valueType));
  150. #if !NET20
  151. if (!HasParameterizedCreatorInternal && underlyingType.Name == FSharpUtils.FSharpMapTypeName)
  152. {
  153. FSharpUtils.EnsureInitialized(underlyingType.Assembly());
  154. _parameterizedCreator = FSharpUtils.CreateMap(keyType, valueType);
  155. }
  156. #endif
  157. }
  158. if (!typeof(IDictionary).IsAssignableFrom(CreatedType))
  159. {
  160. ShouldCreateWrapper = true;
  161. }
  162. DictionaryKeyType = keyType;
  163. DictionaryValueType = valueType;
  164. #if (NET20 || NET35)
  165. if (DictionaryValueType != null && ReflectionUtils.IsNullableType(DictionaryValueType))
  166. {
  167. // bug in .NET 2.0 & 3.5 that Dictionary<TKey, Nullable<TValue>> throws an error when adding null via IDictionary[key] = object
  168. // wrapper will handle calling Add(T) instead
  169. if (ReflectionUtils.InheritsGenericDefinition(CreatedType, typeof(Dictionary<,>), out _))
  170. {
  171. ShouldCreateWrapper = true;
  172. }
  173. }
  174. #endif
  175. if (ImmutableCollectionsUtils.TryBuildImmutableForDictionaryContract(
  176. underlyingType,
  177. DictionaryKeyType,
  178. DictionaryValueType,
  179. out Type immutableCreatedType,
  180. out ObjectConstructor<object> immutableParameterizedCreator))
  181. {
  182. CreatedType = immutableCreatedType;
  183. _parameterizedCreator = immutableParameterizedCreator;
  184. IsReadOnlyOrFixedSize = true;
  185. }
  186. }
  187. internal IWrappedDictionary CreateWrapper(object dictionary)
  188. {
  189. if (_genericWrapperCreator == null)
  190. {
  191. _genericWrapperType = typeof(DictionaryWrapper<,>).MakeGenericType(DictionaryKeyType, DictionaryValueType);
  192. ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType });
  193. _genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParameterizedConstructor(genericWrapperConstructor);
  194. }
  195. return (IWrappedDictionary)_genericWrapperCreator(dictionary);
  196. }
  197. internal IDictionary CreateTemporaryDictionary()
  198. {
  199. if (_genericTemporaryDictionaryCreator == null)
  200. {
  201. Type temporaryDictionaryType = typeof(Dictionary<,>).MakeGenericType(DictionaryKeyType ?? typeof(object), DictionaryValueType ?? typeof(object));
  202. _genericTemporaryDictionaryCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor<object>(temporaryDictionaryType);
  203. }
  204. return (IDictionary)_genericTemporaryDictionaryCreator();
  205. }
  206. }
  207. }