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.

1221 lines
50 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;
  27. using System.Collections.Generic;
  28. using System.ComponentModel;
  29. #if !NET20
  30. using System.Dynamic;
  31. #endif
  32. using System.Diagnostics;
  33. using System.Globalization;
  34. using System.IO;
  35. using System.Security;
  36. using Newtonsoft.Json.Linq;
  37. using Newtonsoft.Json.Utilities;
  38. using System.Runtime.Serialization;
  39. #if NET20
  40. using Newtonsoft.Json.Utilities.LinqBridge;
  41. #else
  42. using System.Linq;
  43. #endif
  44. namespace Newtonsoft.Json.Serialization
  45. {
  46. internal class JsonSerializerInternalWriter : JsonSerializerInternalBase
  47. {
  48. private Type _rootType;
  49. private int _rootLevel;
  50. private readonly List<object> _serializeStack = new List<object>();
  51. public JsonSerializerInternalWriter(JsonSerializer serializer)
  52. : base(serializer)
  53. {
  54. }
  55. public void Serialize(JsonWriter jsonWriter, object value, Type objectType)
  56. {
  57. if (jsonWriter == null)
  58. {
  59. throw new ArgumentNullException(nameof(jsonWriter));
  60. }
  61. _rootType = objectType;
  62. _rootLevel = _serializeStack.Count + 1;
  63. JsonContract contract = GetContractSafe(value);
  64. try
  65. {
  66. if (ShouldWriteReference(value, null, contract, null, null))
  67. {
  68. WriteReference(jsonWriter, value);
  69. }
  70. else
  71. {
  72. SerializeValue(jsonWriter, value, contract, null, null, null);
  73. }
  74. }
  75. catch (Exception ex)
  76. {
  77. if (IsErrorHandled(null, contract, null, null, jsonWriter.Path, ex))
  78. {
  79. HandleError(jsonWriter, 0);
  80. }
  81. else
  82. {
  83. // clear context in case serializer is being used inside a converter
  84. // if the converter wraps the error then not clearing the context will cause this error:
  85. // "Current error context error is different to requested error."
  86. ClearErrorContext();
  87. throw;
  88. }
  89. }
  90. finally
  91. {
  92. // clear root contract to ensure that if level was > 1 then it won't
  93. // accidentally be used for non root values
  94. _rootType = null;
  95. }
  96. }
  97. private JsonSerializerProxy GetInternalSerializer()
  98. {
  99. if (InternalSerializer == null)
  100. {
  101. InternalSerializer = new JsonSerializerProxy(this);
  102. }
  103. return InternalSerializer;
  104. }
  105. private JsonContract GetContractSafe(object value)
  106. {
  107. if (value == null)
  108. {
  109. return null;
  110. }
  111. return Serializer._contractResolver.ResolveContract(value.GetType());
  112. }
  113. private void SerializePrimitive(JsonWriter writer, object value, JsonPrimitiveContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
  114. {
  115. if (contract.TypeCode == PrimitiveTypeCode.Bytes)
  116. {
  117. // if type name handling is enabled then wrap the base64 byte string in an object with the type name
  118. bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Objects, contract, member, containerContract, containerProperty);
  119. if (includeTypeDetails)
  120. {
  121. writer.WriteStartObject();
  122. WriteTypeProperty(writer, contract.CreatedType);
  123. writer.WritePropertyName(JsonTypeReflector.ValuePropertyName, false);
  124. JsonWriter.WriteValue(writer, contract.TypeCode, value);
  125. writer.WriteEndObject();
  126. return;
  127. }
  128. }
  129. JsonWriter.WriteValue(writer, contract.TypeCode, value);
  130. }
  131. private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
  132. {
  133. if (value == null)
  134. {
  135. writer.WriteNull();
  136. return;
  137. }
  138. JsonConverter converter =
  139. member?.Converter ??
  140. containerProperty?.ItemConverter ??
  141. containerContract?.ItemConverter ??
  142. valueContract.Converter ??
  143. Serializer.GetMatchingConverter(valueContract.UnderlyingType) ??
  144. valueContract.InternalConverter;
  145. if (converter != null && converter.CanWrite)
  146. {
  147. SerializeConvertable(writer, converter, value, valueContract, containerContract, containerProperty);
  148. return;
  149. }
  150. switch (valueContract.ContractType)
  151. {
  152. case JsonContractType.Object:
  153. SerializeObject(writer, value, (JsonObjectContract)valueContract, member, containerContract, containerProperty);
  154. break;
  155. case JsonContractType.Array:
  156. JsonArrayContract arrayContract = (JsonArrayContract)valueContract;
  157. if (!arrayContract.IsMultidimensionalArray)
  158. {
  159. SerializeList(writer, (IEnumerable)value, arrayContract, member, containerContract, containerProperty);
  160. }
  161. else
  162. {
  163. SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty);
  164. }
  165. break;
  166. case JsonContractType.Primitive:
  167. SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, containerContract, containerProperty);
  168. break;
  169. case JsonContractType.String:
  170. SerializeString(writer, value, (JsonStringContract)valueContract);
  171. break;
  172. case JsonContractType.Dictionary:
  173. JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract;
  174. SerializeDictionary(writer, (value is IDictionary) ? (IDictionary)value : dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty);
  175. break;
  176. #if !NET20
  177. case JsonContractType.Dynamic:
  178. SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, containerContract, containerProperty);
  179. break;
  180. #endif
  181. #if HAVE_BINARY_SERIALIZATION
  182. case JsonContractType.Serializable:
  183. SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, containerContract, containerProperty);
  184. break;
  185. #endif
  186. case JsonContractType.Linq:
  187. ((JToken)value).WriteTo(writer, Serializer.Converters.ToArray());
  188. break;
  189. }
  190. }
  191. private bool? ResolveIsReference(JsonContract contract, JsonProperty property, JsonContainerContract collectionContract, JsonProperty containerProperty)
  192. {
  193. bool? isReference = null;
  194. // value could be coming from a dictionary or array and not have a property
  195. if (property != null)
  196. {
  197. isReference = property.IsReference;
  198. }
  199. if (isReference == null && containerProperty != null)
  200. {
  201. isReference = containerProperty.ItemIsReference;
  202. }
  203. if (isReference == null && collectionContract != null)
  204. {
  205. isReference = collectionContract.ItemIsReference;
  206. }
  207. if (isReference == null)
  208. {
  209. isReference = contract.IsReference;
  210. }
  211. return isReference;
  212. }
  213. private bool ShouldWriteReference(object value, JsonProperty property, JsonContract valueContract, JsonContainerContract collectionContract, JsonProperty containerProperty)
  214. {
  215. if (value == null)
  216. {
  217. return false;
  218. }
  219. if (valueContract.ContractType == JsonContractType.Primitive || valueContract.ContractType == JsonContractType.String)
  220. {
  221. return false;
  222. }
  223. bool? isReference = ResolveIsReference(valueContract, property, collectionContract, containerProperty);
  224. if (isReference == null)
  225. {
  226. if (valueContract.ContractType == JsonContractType.Array)
  227. {
  228. isReference = HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Arrays);
  229. }
  230. else
  231. {
  232. isReference = HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Objects);
  233. }
  234. }
  235. if (!isReference.GetValueOrDefault())
  236. {
  237. return false;
  238. }
  239. return Serializer.GetReferenceResolver().IsReferenced(this, value);
  240. }
  241. private bool ShouldWriteProperty(object memberValue, JsonObjectContract containerContract, JsonProperty property)
  242. {
  243. if (memberValue == null && ResolvedNullValueHandling(containerContract, property) == NullValueHandling.Ignore)
  244. {
  245. return false;
  246. }
  247. if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Ignore)
  248. && MiscellaneousUtils.ValueEquals(memberValue, property.GetResolvedDefaultValue()))
  249. {
  250. return false;
  251. }
  252. return true;
  253. }
  254. private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
  255. {
  256. if (value == null || contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String)
  257. {
  258. return true;
  259. }
  260. ReferenceLoopHandling? referenceLoopHandling = null;
  261. if (property != null)
  262. {
  263. referenceLoopHandling = property.ReferenceLoopHandling;
  264. }
  265. if (referenceLoopHandling == null && containerProperty != null)
  266. {
  267. referenceLoopHandling = containerProperty.ItemReferenceLoopHandling;
  268. }
  269. if (referenceLoopHandling == null && containerContract != null)
  270. {
  271. referenceLoopHandling = containerContract.ItemReferenceLoopHandling;
  272. }
  273. bool exists = (Serializer._equalityComparer != null)
  274. ? _serializeStack.Contains(value, Serializer._equalityComparer)
  275. : _serializeStack.Contains(value);
  276. if (exists)
  277. {
  278. string message = "Self referencing loop detected";
  279. if (property != null)
  280. {
  281. message += " for property '{0}'".FormatWith(CultureInfo.InvariantCulture, property.PropertyName);
  282. }
  283. message += " with type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType());
  284. switch (referenceLoopHandling.GetValueOrDefault(Serializer._referenceLoopHandling))
  285. {
  286. case ReferenceLoopHandling.Error:
  287. throw JsonSerializationException.Create(null, writer.ContainerPath, message, null);
  288. case ReferenceLoopHandling.Ignore:
  289. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  290. {
  291. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Skipping serializing self referenced value."), null);
  292. }
  293. return false;
  294. case ReferenceLoopHandling.Serialize:
  295. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  296. {
  297. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Serializing self referenced value."), null);
  298. }
  299. return true;
  300. }
  301. }
  302. return true;
  303. }
  304. private void WriteReference(JsonWriter writer, object value)
  305. {
  306. string reference = GetReference(writer, value);
  307. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
  308. {
  309. TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference to Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, value.GetType())), null);
  310. }
  311. writer.WriteStartObject();
  312. writer.WritePropertyName(JsonTypeReflector.RefPropertyName, false);
  313. writer.WriteValue(reference);
  314. writer.WriteEndObject();
  315. }
  316. private string GetReference(JsonWriter writer, object value)
  317. {
  318. try
  319. {
  320. string reference = Serializer.GetReferenceResolver().GetReference(this, value);
  321. return reference;
  322. }
  323. catch (Exception ex)
  324. {
  325. throw JsonSerializationException.Create(null, writer.ContainerPath, "Error writing object reference for '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()), ex);
  326. }
  327. }
  328. internal static bool TryConvertToString(object value, Type type, out string s)
  329. {
  330. if (JsonTypeReflector.CanTypeDescriptorConvertString(type, out TypeConverter converter))
  331. {
  332. s = converter.ConvertToInvariantString(value);
  333. return true;
  334. }
  335. #if (DOTNET || PORTABLE)
  336. if (value is Guid || value is Uri || value is TimeSpan)
  337. {
  338. s = value.ToString();
  339. return true;
  340. }
  341. #endif
  342. type = value as Type;
  343. if (type != null)
  344. {
  345. s = type.AssemblyQualifiedName;
  346. return true;
  347. }
  348. s = null;
  349. return false;
  350. }
  351. private void SerializeString(JsonWriter writer, object value, JsonStringContract contract)
  352. {
  353. OnSerializing(writer, contract, value);
  354. TryConvertToString(value, contract.UnderlyingType, out string s);
  355. writer.WriteValue(s);
  356. OnSerialized(writer, contract, value);
  357. }
  358. private void OnSerializing(JsonWriter writer, JsonContract contract, object value)
  359. {
  360. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
  361. {
  362. TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null);
  363. }
  364. contract.InvokeOnSerializing(value, Serializer._context);
  365. }
  366. private void OnSerialized(JsonWriter writer, JsonContract contract, object value)
  367. {
  368. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
  369. {
  370. TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null);
  371. }
  372. contract.InvokeOnSerialized(value, Serializer._context);
  373. }
  374. private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  375. {
  376. OnSerializing(writer, contract, value);
  377. _serializeStack.Add(value);
  378. WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);
  379. int initialDepth = writer.Top;
  380. for (int index = 0; index < contract.Properties.Count; index++)
  381. {
  382. JsonProperty property = contract.Properties[index];
  383. try
  384. {
  385. if (!CalculatePropertyValues(writer, value, contract, member, property, out JsonContract memberContract, out object memberValue))
  386. {
  387. continue;
  388. }
  389. property.WritePropertyName(writer);
  390. SerializeValue(writer, memberValue, memberContract, property, contract, member);
  391. }
  392. catch (Exception ex)
  393. {
  394. if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex))
  395. {
  396. HandleError(writer, initialDepth);
  397. }
  398. else
  399. {
  400. throw;
  401. }
  402. }
  403. }
  404. IEnumerable<KeyValuePair<object, object>> extensionData = contract.ExtensionDataGetter?.Invoke(value);
  405. if (extensionData != null)
  406. {
  407. foreach (KeyValuePair<object, object> e in extensionData)
  408. {
  409. JsonContract keyContract = GetContractSafe(e.Key);
  410. JsonContract valueContract = GetContractSafe(e.Value);
  411. bool escape;
  412. string propertyName = GetPropertyName(writer, e.Key, keyContract, out escape);
  413. propertyName = (contract.ExtensionDataNameResolver != null)
  414. ? contract.ExtensionDataNameResolver(propertyName)
  415. : propertyName;
  416. if (ShouldWriteReference(e.Value, null, valueContract, contract, member))
  417. {
  418. writer.WritePropertyName(propertyName);
  419. WriteReference(writer, e.Value);
  420. }
  421. else
  422. {
  423. if (!CheckForCircularReference(writer, e.Value, null, valueContract, contract, member))
  424. {
  425. continue;
  426. }
  427. writer.WritePropertyName(propertyName);
  428. SerializeValue(writer, e.Value, valueContract, null, contract, member);
  429. }
  430. }
  431. }
  432. writer.WriteEndObject();
  433. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  434. OnSerialized(writer, contract, value);
  435. }
  436. private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue)
  437. {
  438. if (!property.Ignored && property.Readable && ShouldSerialize(writer, property, value) && IsSpecified(writer, property, value))
  439. {
  440. if (property.PropertyContract == null)
  441. {
  442. property.PropertyContract = Serializer._contractResolver.ResolveContract(property.PropertyType);
  443. }
  444. memberValue = property.ValueProvider.GetValue(value);
  445. memberContract = (property.PropertyContract.IsSealed) ? property.PropertyContract : GetContractSafe(memberValue);
  446. if (ShouldWriteProperty(memberValue, contract as JsonObjectContract, property))
  447. {
  448. if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
  449. {
  450. property.WritePropertyName(writer);
  451. WriteReference(writer, memberValue);
  452. return false;
  453. }
  454. if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member))
  455. {
  456. return false;
  457. }
  458. if (memberValue == null)
  459. {
  460. JsonObjectContract objectContract = contract as JsonObjectContract;
  461. Required resolvedRequired = property._required ?? objectContract?.ItemRequired ?? Required.Default;
  462. if (resolvedRequired == Required.Always)
  463. {
  464. throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
  465. }
  466. if (resolvedRequired == Required.DisallowNull)
  467. {
  468. throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a non-null value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
  469. }
  470. }
  471. return true;
  472. }
  473. }
  474. memberContract = null;
  475. memberValue = null;
  476. return false;
  477. }
  478. private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  479. {
  480. writer.WriteStartObject();
  481. bool isReference = ResolveIsReference(contract, member, collectionContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Objects);
  482. // don't make readonly fields that aren't creator parameters the referenced value because they can't be deserialized to
  483. if (isReference && (member == null || member.Writable || HasCreatorParameter(collectionContract, member)))
  484. {
  485. WriteReferenceIdProperty(writer, contract.UnderlyingType, value);
  486. }
  487. if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, containerProperty))
  488. {
  489. WriteTypeProperty(writer, contract.UnderlyingType);
  490. }
  491. }
  492. private bool HasCreatorParameter(JsonContainerContract contract, JsonProperty property)
  493. {
  494. if (!(contract is JsonObjectContract objectContract))
  495. {
  496. return false;
  497. }
  498. return objectContract.CreatorParameters.Contains(property.PropertyName);
  499. }
  500. private void WriteReferenceIdProperty(JsonWriter writer, Type type, object value)
  501. {
  502. string reference = GetReference(writer, value);
  503. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  504. {
  505. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, type)), null);
  506. }
  507. writer.WritePropertyName(JsonTypeReflector.IdPropertyName, false);
  508. writer.WriteValue(reference);
  509. }
  510. private void WriteTypeProperty(JsonWriter writer, Type type)
  511. {
  512. string typeName = ReflectionUtils.GetTypeName(type, Serializer._typeNameAssemblyFormatHandling, Serializer._serializationBinder);
  513. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  514. {
  515. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing type name '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, typeName, type)), null);
  516. }
  517. writer.WritePropertyName(JsonTypeReflector.TypePropertyName, false);
  518. writer.WriteValue(typeName);
  519. }
  520. private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
  521. {
  522. return ((value & flag) == flag);
  523. }
  524. private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag)
  525. {
  526. return ((value & flag) == flag);
  527. }
  528. private bool HasFlag(TypeNameHandling value, TypeNameHandling flag)
  529. {
  530. return ((value & flag) == flag);
  531. }
  532. private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty)
  533. {
  534. if (ShouldWriteReference(value, null, contract, collectionContract, containerProperty))
  535. {
  536. WriteReference(writer, value);
  537. }
  538. else
  539. {
  540. if (!CheckForCircularReference(writer, value, null, contract, collectionContract, containerProperty))
  541. {
  542. return;
  543. }
  544. _serializeStack.Add(value);
  545. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
  546. {
  547. TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null);
  548. }
  549. converter.WriteJson(writer, value, GetInternalSerializer());
  550. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
  551. {
  552. TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null);
  553. }
  554. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  555. }
  556. }
  557. private void SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  558. {
  559. object underlyingList = values is IWrappedCollection wrappedCollection ? wrappedCollection.UnderlyingCollection : values;
  560. OnSerializing(writer, contract, underlyingList);
  561. _serializeStack.Add(underlyingList);
  562. bool hasWrittenMetadataObject = WriteStartArray(writer, underlyingList, contract, member, collectionContract, containerProperty);
  563. writer.WriteStartArray();
  564. int initialDepth = writer.Top;
  565. int index = 0;
  566. // note that an error in the IEnumerable won't be caught
  567. foreach (object value in values)
  568. {
  569. try
  570. {
  571. JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
  572. if (ShouldWriteReference(value, null, valueContract, contract, member))
  573. {
  574. WriteReference(writer, value);
  575. }
  576. else
  577. {
  578. if (CheckForCircularReference(writer, value, null, valueContract, contract, member))
  579. {
  580. SerializeValue(writer, value, valueContract, null, contract, member);
  581. }
  582. }
  583. }
  584. catch (Exception ex)
  585. {
  586. if (IsErrorHandled(underlyingList, contract, index, null, writer.ContainerPath, ex))
  587. {
  588. HandleError(writer, initialDepth);
  589. }
  590. else
  591. {
  592. throw;
  593. }
  594. }
  595. finally
  596. {
  597. index++;
  598. }
  599. }
  600. writer.WriteEndArray();
  601. if (hasWrittenMetadataObject)
  602. {
  603. writer.WriteEndObject();
  604. }
  605. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  606. OnSerialized(writer, contract, underlyingList);
  607. }
  608. private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  609. {
  610. OnSerializing(writer, contract, values);
  611. _serializeStack.Add(values);
  612. bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, containerProperty);
  613. SerializeMultidimensionalArray(writer, values, contract, member, writer.Top, CollectionUtils.ArrayEmpty<int>());
  614. if (hasWrittenMetadataObject)
  615. {
  616. writer.WriteEndObject();
  617. }
  618. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  619. OnSerialized(writer, contract, values);
  620. }
  621. private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, int initialDepth, int[] indices)
  622. {
  623. int dimension = indices.Length;
  624. int[] newIndices = new int[dimension + 1];
  625. for (int i = 0; i < dimension; i++)
  626. {
  627. newIndices[i] = indices[i];
  628. }
  629. writer.WriteStartArray();
  630. for (int i = values.GetLowerBound(dimension); i <= values.GetUpperBound(dimension); i++)
  631. {
  632. newIndices[dimension] = i;
  633. bool isTopLevel = (newIndices.Length == values.Rank);
  634. if (isTopLevel)
  635. {
  636. object value = values.GetValue(newIndices);
  637. try
  638. {
  639. JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
  640. if (ShouldWriteReference(value, null, valueContract, contract, member))
  641. {
  642. WriteReference(writer, value);
  643. }
  644. else
  645. {
  646. if (CheckForCircularReference(writer, value, null, valueContract, contract, member))
  647. {
  648. SerializeValue(writer, value, valueContract, null, contract, member);
  649. }
  650. }
  651. }
  652. catch (Exception ex)
  653. {
  654. if (IsErrorHandled(values, contract, i, null, writer.ContainerPath, ex))
  655. {
  656. HandleError(writer, initialDepth + 1);
  657. }
  658. else
  659. {
  660. throw;
  661. }
  662. }
  663. }
  664. else
  665. {
  666. SerializeMultidimensionalArray(writer, values, contract, member, initialDepth + 1, newIndices);
  667. }
  668. }
  669. writer.WriteEndArray();
  670. }
  671. private bool WriteStartArray(JsonWriter writer, object values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
  672. {
  673. bool isReference = ResolveIsReference(contract, member, containerContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Arrays);
  674. // don't make readonly fields that aren't creator parameters the referenced value because they can't be deserialized to
  675. isReference = (isReference && (member == null || member.Writable || HasCreatorParameter(containerContract, member)));
  676. bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, containerContract, containerProperty);
  677. bool writeMetadataObject = isReference || includeTypeDetails;
  678. if (writeMetadataObject)
  679. {
  680. writer.WriteStartObject();
  681. if (isReference)
  682. {
  683. WriteReferenceIdProperty(writer, contract.UnderlyingType, values);
  684. }
  685. if (includeTypeDetails)
  686. {
  687. WriteTypeProperty(writer, values.GetType());
  688. }
  689. writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName, false);
  690. }
  691. if (contract.ItemContract == null)
  692. {
  693. contract.ItemContract = Serializer._contractResolver.ResolveContract(contract.CollectionItemType ?? typeof(object));
  694. }
  695. return writeMetadataObject;
  696. }
  697. #if HAVE_BINARY_SERIALIZATION
  698. #if !NET20
  699. [SecuritySafeCritical]
  700. #endif
  701. private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  702. {
  703. if (!JsonTypeReflector.FullyTrusted)
  704. {
  705. string message = @"Type '{0}' implements ISerializable but cannot be serialized using the ISerializable interface because the current application is not fully trusted and ISerializable can expose secure data." + Environment.NewLine +
  706. @"To fix this error either change the environment to be fully trusted, change the application to not deserialize the type, add JsonObjectAttribute to the type or change the JsonSerializer setting ContractResolver to use a new DefaultContractResolver with IgnoreSerializableInterface set to true." + Environment.NewLine;
  707. message = message.FormatWith(CultureInfo.InvariantCulture, value.GetType());
  708. throw JsonSerializationException.Create(null, writer.ContainerPath, message, null);
  709. }
  710. OnSerializing(writer, contract, value);
  711. _serializeStack.Add(value);
  712. WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);
  713. SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter());
  714. value.GetObjectData(serializationInfo, Serializer._context);
  715. foreach (SerializationEntry serializationEntry in serializationInfo)
  716. {
  717. JsonContract valueContract = GetContractSafe(serializationEntry.Value);
  718. if (ShouldWriteReference(serializationEntry.Value, null, valueContract, contract, member))
  719. {
  720. writer.WritePropertyName(serializationEntry.Name);
  721. WriteReference(writer, serializationEntry.Value);
  722. }
  723. else if (CheckForCircularReference(writer, serializationEntry.Value, null, valueContract, contract, member))
  724. {
  725. writer.WritePropertyName(serializationEntry.Name);
  726. SerializeValue(writer, serializationEntry.Value, valueContract, null, contract, member);
  727. }
  728. }
  729. writer.WriteEndObject();
  730. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  731. OnSerialized(writer, contract, value);
  732. }
  733. #endif
  734. #if !NET20
  735. private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  736. {
  737. OnSerializing(writer, contract, value);
  738. _serializeStack.Add(value);
  739. WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);
  740. int initialDepth = writer.Top;
  741. for (int index = 0; index < contract.Properties.Count; index++)
  742. {
  743. JsonProperty property = contract.Properties[index];
  744. // only write non-dynamic properties that have an explicit attribute
  745. if (property.HasMemberAttribute)
  746. {
  747. try
  748. {
  749. if (!CalculatePropertyValues(writer, value, contract, member, property, out JsonContract memberContract, out object memberValue))
  750. {
  751. continue;
  752. }
  753. property.WritePropertyName(writer);
  754. SerializeValue(writer, memberValue, memberContract, property, contract, member);
  755. }
  756. catch (Exception ex)
  757. {
  758. if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex))
  759. {
  760. HandleError(writer, initialDepth);
  761. }
  762. else
  763. {
  764. throw;
  765. }
  766. }
  767. }
  768. }
  769. foreach (string memberName in value.GetDynamicMemberNames())
  770. {
  771. if (contract.TryGetMember(value, memberName, out object memberValue))
  772. {
  773. try
  774. {
  775. JsonContract valueContract = GetContractSafe(memberValue);
  776. if (!ShouldWriteDynamicProperty(memberValue))
  777. {
  778. continue;
  779. }
  780. if (CheckForCircularReference(writer, memberValue, null, valueContract, contract, member))
  781. {
  782. string resolvedPropertyName = (contract.PropertyNameResolver != null)
  783. ? contract.PropertyNameResolver(memberName)
  784. : memberName;
  785. writer.WritePropertyName(resolvedPropertyName);
  786. SerializeValue(writer, memberValue, valueContract, null, contract, member);
  787. }
  788. }
  789. catch (Exception ex)
  790. {
  791. if (IsErrorHandled(value, contract, memberName, null, writer.ContainerPath, ex))
  792. {
  793. HandleError(writer, initialDepth);
  794. }
  795. else
  796. {
  797. throw;
  798. }
  799. }
  800. }
  801. }
  802. writer.WriteEndObject();
  803. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  804. OnSerialized(writer, contract, value);
  805. }
  806. #endif
  807. private bool ShouldWriteDynamicProperty(object memberValue)
  808. {
  809. if (Serializer._nullValueHandling == NullValueHandling.Ignore && memberValue == null)
  810. {
  811. return false;
  812. }
  813. if (HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Ignore) &&
  814. (memberValue == null || MiscellaneousUtils.ValueEquals(memberValue, ReflectionUtils.GetDefaultValue(memberValue.GetType()))))
  815. {
  816. return false;
  817. }
  818. return true;
  819. }
  820. private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
  821. {
  822. TypeNameHandling resolvedTypeNameHandling =
  823. member?.TypeNameHandling
  824. ?? containerProperty?.ItemTypeNameHandling
  825. ?? containerContract?.ItemTypeNameHandling
  826. ?? Serializer._typeNameHandling;
  827. if (HasFlag(resolvedTypeNameHandling, typeNameHandlingFlag))
  828. {
  829. return true;
  830. }
  831. // instance type and the property's type's contract default type are different (no need to put the type in JSON because the type will be created by default)
  832. if (HasFlag(resolvedTypeNameHandling, TypeNameHandling.Auto))
  833. {
  834. if (member != null)
  835. {
  836. if (contract.NonNullableUnderlyingType != member.PropertyContract.CreatedType)
  837. {
  838. return true;
  839. }
  840. }
  841. else if (containerContract != null)
  842. {
  843. if (containerContract.ItemContract == null || contract.NonNullableUnderlyingType != containerContract.ItemContract.CreatedType)
  844. {
  845. return true;
  846. }
  847. }
  848. else if (_rootType != null && _serializeStack.Count == _rootLevel)
  849. {
  850. JsonContract rootContract = Serializer._contractResolver.ResolveContract(_rootType);
  851. if (contract.NonNullableUnderlyingType != rootContract.CreatedType)
  852. {
  853. return true;
  854. }
  855. }
  856. }
  857. return false;
  858. }
  859. private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
  860. {
  861. object underlyingDictionary = values is IWrappedDictionary wrappedDictionary ? wrappedDictionary.UnderlyingDictionary : values;
  862. OnSerializing(writer, contract, underlyingDictionary);
  863. _serializeStack.Add(underlyingDictionary);
  864. WriteObjectStart(writer, underlyingDictionary, contract, member, collectionContract, containerProperty);
  865. if (contract.ItemContract == null)
  866. {
  867. contract.ItemContract = Serializer._contractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object));
  868. }
  869. if (contract.KeyContract == null)
  870. {
  871. contract.KeyContract = Serializer._contractResolver.ResolveContract(contract.DictionaryKeyType ?? typeof(object));
  872. }
  873. int initialDepth = writer.Top;
  874. // Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
  875. IDictionaryEnumerator e = values.GetEnumerator();
  876. try
  877. {
  878. while (e.MoveNext())
  879. {
  880. DictionaryEntry entry = e.Entry;
  881. string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out bool escape);
  882. propertyName = (contract.DictionaryKeyResolver != null)
  883. ? contract.DictionaryKeyResolver(propertyName)
  884. : propertyName;
  885. try
  886. {
  887. object value = entry.Value;
  888. JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);
  889. if (ShouldWriteReference(value, null, valueContract, contract, member))
  890. {
  891. writer.WritePropertyName(propertyName, escape);
  892. WriteReference(writer, value);
  893. }
  894. else
  895. {
  896. if (!CheckForCircularReference(writer, value, null, valueContract, contract, member))
  897. {
  898. continue;
  899. }
  900. writer.WritePropertyName(propertyName, escape);
  901. SerializeValue(writer, value, valueContract, null, contract, member);
  902. }
  903. }
  904. catch (Exception ex)
  905. {
  906. if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
  907. {
  908. HandleError(writer, initialDepth);
  909. }
  910. else
  911. {
  912. throw;
  913. }
  914. }
  915. }
  916. }
  917. finally
  918. {
  919. (e as IDisposable)?.Dispose();
  920. }
  921. writer.WriteEndObject();
  922. _serializeStack.RemoveAt(_serializeStack.Count - 1);
  923. OnSerialized(writer, contract, underlyingDictionary);
  924. }
  925. private string GetPropertyName(JsonWriter writer, object name, JsonContract contract, out bool escape)
  926. {
  927. if (contract.ContractType == JsonContractType.Primitive)
  928. {
  929. JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract;
  930. switch (primitiveContract.TypeCode)
  931. {
  932. case PrimitiveTypeCode.DateTime:
  933. case PrimitiveTypeCode.DateTimeNullable:
  934. {
  935. DateTime dt = DateTimeUtils.EnsureDateTime((DateTime)name, writer.DateTimeZoneHandling);
  936. escape = false;
  937. StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
  938. DateTimeUtils.WriteDateTimeString(sw, dt, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
  939. return sw.ToString();
  940. }
  941. #if !NET20
  942. case PrimitiveTypeCode.DateTimeOffset:
  943. case PrimitiveTypeCode.DateTimeOffsetNullable:
  944. {
  945. escape = false;
  946. StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
  947. DateTimeUtils.WriteDateTimeOffsetString(sw, (DateTimeOffset)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
  948. return sw.ToString();
  949. }
  950. #endif
  951. case PrimitiveTypeCode.Double:
  952. case PrimitiveTypeCode.DoubleNullable:
  953. {
  954. double d = (double)name;
  955. escape = false;
  956. return d.ToString("R", CultureInfo.InvariantCulture);
  957. }
  958. case PrimitiveTypeCode.Single:
  959. case PrimitiveTypeCode.SingleNullable:
  960. {
  961. float f = (float)name;
  962. escape = false;
  963. return f.ToString("R", CultureInfo.InvariantCulture);
  964. }
  965. default:
  966. {
  967. escape = true;
  968. if (primitiveContract.IsEnum && EnumUtils.TryToString(primitiveContract.NonNullableUnderlyingType, name, false, out string enumName))
  969. {
  970. return enumName;
  971. }
  972. return Convert.ToString(name, CultureInfo.InvariantCulture);
  973. }
  974. }
  975. }
  976. else if (TryConvertToString(name, name.GetType(), out string propertyName))
  977. {
  978. escape = true;
  979. return propertyName;
  980. }
  981. else
  982. {
  983. escape = true;
  984. return name.ToString();
  985. }
  986. }
  987. private void HandleError(JsonWriter writer, int initialDepth)
  988. {
  989. ClearErrorContext();
  990. if (writer.WriteState == WriteState.Property)
  991. {
  992. writer.WriteNull();
  993. }
  994. while (writer.Top > initialDepth)
  995. {
  996. writer.WriteEnd();
  997. }
  998. }
  999. private bool ShouldSerialize(JsonWriter writer, JsonProperty property, object target)
  1000. {
  1001. if (property.ShouldSerialize == null)
  1002. {
  1003. return true;
  1004. }
  1005. bool shouldSerialize = property.ShouldSerialize(target);
  1006. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  1007. {
  1008. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "ShouldSerialize result for property '{0}' on {1}: {2}".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, property.DeclaringType, shouldSerialize)), null);
  1009. }
  1010. return shouldSerialize;
  1011. }
  1012. private bool IsSpecified(JsonWriter writer, JsonProperty property, object target)
  1013. {
  1014. if (property.GetIsSpecified == null)
  1015. {
  1016. return true;
  1017. }
  1018. bool isSpecified = property.GetIsSpecified(target);
  1019. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
  1020. {
  1021. TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "IsSpecified result for property '{0}' on {1}: {2}".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, property.DeclaringType, isSpecified)), null);
  1022. }
  1023. return isSpecified;
  1024. }
  1025. }
  1026. }