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.

158 lines
6.1 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.Diagnostics;
  28. using System.Runtime.CompilerServices;
  29. using Newtonsoft.Json.Utilities;
  30. namespace Newtonsoft.Json.Serialization
  31. {
  32. internal abstract class JsonSerializerInternalBase
  33. {
  34. private class ReferenceEqualsEqualityComparer : IEqualityComparer<object>
  35. {
  36. bool IEqualityComparer<object>.Equals(object x, object y)
  37. {
  38. return ReferenceEquals(x, y);
  39. }
  40. int IEqualityComparer<object>.GetHashCode(object obj)
  41. {
  42. // put objects in a bucket based on their reference
  43. return RuntimeHelpers.GetHashCode(obj);
  44. }
  45. }
  46. private ErrorContext _currentErrorContext;
  47. private BidirectionalDictionary<string, object> _mappings;
  48. internal readonly JsonSerializer Serializer;
  49. internal readonly ITraceWriter TraceWriter;
  50. protected JsonSerializerProxy InternalSerializer;
  51. protected JsonSerializerInternalBase(JsonSerializer serializer)
  52. {
  53. ValidationUtils.ArgumentNotNull(serializer, nameof(serializer));
  54. Serializer = serializer;
  55. TraceWriter = serializer.TraceWriter;
  56. }
  57. internal BidirectionalDictionary<string, object> DefaultReferenceMappings
  58. {
  59. get
  60. {
  61. // override equality comparer for object key dictionary
  62. // object will be modified as it deserializes and might have mutable hashcode
  63. if (_mappings == null)
  64. {
  65. _mappings = new BidirectionalDictionary<string, object>(
  66. EqualityComparer<string>.Default,
  67. new ReferenceEqualsEqualityComparer(),
  68. "A different value already has the Id '{0}'.",
  69. "A different Id has already been assigned for value '{0}'. This error may be caused by an object being reused multiple times during deserialization and can be fixed with the setting ObjectCreationHandling.Replace.");
  70. }
  71. return _mappings;
  72. }
  73. }
  74. protected NullValueHandling ResolvedNullValueHandling(JsonObjectContract containerContract, JsonProperty property)
  75. {
  76. NullValueHandling resolvedNullValueHandling =
  77. property.NullValueHandling
  78. ?? containerContract?.ItemNullValueHandling
  79. ?? Serializer._nullValueHandling;
  80. return resolvedNullValueHandling;
  81. }
  82. private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)
  83. {
  84. if (_currentErrorContext == null)
  85. {
  86. _currentErrorContext = new ErrorContext(currentObject, member, path, error);
  87. }
  88. if (_currentErrorContext.Error != error)
  89. {
  90. throw new InvalidOperationException("Current error context error is different to requested error.");
  91. }
  92. return _currentErrorContext;
  93. }
  94. protected void ClearErrorContext()
  95. {
  96. if (_currentErrorContext == null)
  97. {
  98. throw new InvalidOperationException("Could not clear error context. Error context is already null.");
  99. }
  100. _currentErrorContext = null;
  101. }
  102. protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, IJsonLineInfo lineInfo, string path, Exception ex)
  103. {
  104. ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex);
  105. if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Error && !errorContext.Traced)
  106. {
  107. // only write error once
  108. errorContext.Traced = true;
  109. // kind of a hack but meh. might clean this up later
  110. string message = (GetType() == typeof(JsonSerializerInternalWriter)) ? "Error serializing" : "Error deserializing";
  111. if (contract != null)
  112. {
  113. message += " " + contract.UnderlyingType;
  114. }
  115. message += ". " + ex.Message;
  116. // add line information to non-json.net exception message
  117. if (!(ex is JsonException))
  118. {
  119. message = JsonPosition.FormatMessage(lineInfo, path, message);
  120. }
  121. TraceWriter.Trace(TraceLevel.Error, message, ex);
  122. }
  123. // attribute method is non-static so don't invoke if no object
  124. if (contract != null && currentObject != null)
  125. {
  126. contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
  127. }
  128. if (!errorContext.Handled)
  129. {
  130. Serializer.OnError(new ErrorEventArgs(currentObject, errorContext));
  131. }
  132. return errorContext.Handled;
  133. }
  134. }
  135. }