<value>The attribute '{0}' cannot exist more than once on '{1}'.</value>
</data>
<data name="SerializeUnableToSerialize" xml:space="preserve">
- <value>The object or value could not be serialized. Path: {0}.</value>
+ <value>The object or value could not be serialized.</value>
</data>
<data name="FormatByte" xml:space="preserve">
<value>Either the JSON value is not in a supported format, or is out of bounds for an unsigned byte.</value>
}
/// <summary>
+ /// Specifies that 'try' logic should append Path information to the exception message.
+ /// </summary>
+ internal bool AppendPathInformation { get; set; }
+
+ /// <summary>
/// Sets the <see cref="SerializationInfo"/> with information about the exception.
/// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
/// <summary>
/// Determines how a given class is treated when it is (de)serialized.
/// </summary>
- internal enum ClassType
+ /// <remarks>
+ /// Although bit flags are used, a given ClassType can only be one value.
+ /// Bit flags are used to efficiently compare against more than one value.
+ /// </remarks>
+ internal enum ClassType : byte
{
// typeof(object)
- Unknown = 0,
+ Unknown = 0x1,
// POCO or rich data type
- Object = 1,
+ Object = 0x2,
// Value or object with a converter.
- Value = 2,
+ Value = 0x4,
// IEnumerable
- Enumerable = 3,
+ Enumerable = 0x8,
// IDictionary
- Dictionary = 4,
+ Dictionary = 0x10,
// Is deserialized by passing a IDictionary to its constructor
// i.e. immutable dictionaries, Hashtable, SortedList,
- IDictionaryConstructible = 5,
+ IDictionaryConstructible = 0x20,
}
}
private static readonly JsonDictionaryConverter s_jsonIDictionaryConverter = new DefaultIDictionaryConverter();
private static readonly JsonDictionaryConverter s_jsonImmutableDictionaryConverter = new DefaultImmutableDictionaryConverter();
- public static readonly JsonPropertyInfo s_missingProperty = new JsonPropertyInfoNotNullable<object, object, object, object>();
+ public static readonly JsonPropertyInfo s_missingProperty = GetMissingProperty();
private JsonClassInfo _elementClassInfo;
private JsonClassInfo _runtimeClassInfo;
public abstract JsonConverter ConverterBase { get; set; }
+ private static JsonPropertyInfo GetMissingProperty()
+ {
+ JsonPropertyInfo info = new JsonPropertyInfoNotNullable<object, object, object, object>();
+ info.IsPropertyPolicy = false;
+ info.ShouldDeserialize = false;
+ info.ShouldSerialize = false;
+ return info;
+ }
+
// Copy any settings defined at run-time to the new property.
public void CopyRuntimeSettingsTo(JsonPropertyInfo other)
{
public bool HasGetter { get; set; }
public bool HasSetter { get; set; }
+ public bool HasInternalConverter { get; private set; }
+
public virtual void Initialize(
Type parentClassType,
Type declaredPropertyType,
{
ConverterBase = converter;
+ HasInternalConverter = (converter.GetType().Assembly == GetType().Assembly);
+
// Avoid calling GetClassType since it will re-ask if there is a converter which is slow.
if (runtimePropertyType == typeof(object))
{
// Options can be referenced here since all JsonPropertyInfos originate from a JsonClassInfo that is cached on JsonSerializerOptions.
protected JsonSerializerOptions Options { get; set; }
- protected abstract void OnRead(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
- protected abstract void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
+ protected abstract void OnRead(ref ReadStack state, ref Utf8JsonReader reader);
+ protected abstract void OnReadEnumerable(ref ReadStack state, ref Utf8JsonReader reader);
protected abstract void OnWrite(ref WriteStackFrame current, Utf8JsonWriter writer);
protected virtual void OnWriteDictionary(ref WriteStackFrame current, Utf8JsonWriter writer) { }
protected abstract void OnWriteEnumerable(ref WriteStackFrame current, Utf8JsonWriter writer);
// Forward the setter to the value-based JsonPropertyInfo.
propertyInfo.ReadEnumerable(tokenType, ref state, ref reader);
}
+ // For performance on release build, don't verify converter correctness for internal converters.
+ else if (HasInternalConverter)
+ {
+#if DEBUG
+ JsonTokenType originalTokenType = reader.TokenType;
+ int originalDepth = reader.CurrentDepth;
+ long originalBytesConsumed = reader.BytesConsumed;
+#endif
+
+ OnRead(ref state, ref reader);
+
+#if DEBUG
+ VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref reader);
+#endif
+ }
else
{
JsonTokenType originalTokenType = reader.TokenType;
int originalDepth = reader.CurrentDepth;
long originalBytesConsumed = reader.BytesConsumed;
- OnRead(tokenType, ref state, ref reader);
+ OnRead(ref state, ref reader);
- VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref state, ref reader);
+ VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref reader);
}
}
{
Debug.Assert(ShouldDeserialize);
- JsonTokenType originalTokenType = reader.TokenType;
- int originalDepth = reader.CurrentDepth;
- long originalBytesConsumed = reader.BytesConsumed;
+ // For performance on release build, don't verify converter correctness for internal converters.
+ if (HasInternalConverter)
+ {
+#if DEBUG
+ JsonTokenType originalTokenType = reader.TokenType;
+ int originalDepth = reader.CurrentDepth;
+ long originalBytesConsumed = reader.BytesConsumed;
+#endif
+
+ OnReadEnumerable(ref state, ref reader);
- OnReadEnumerable(tokenType, ref state, ref reader);
+#if DEBUG
+ VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref reader);
+#endif
+ }
+ else
+ {
+ JsonTokenType originalTokenType = reader.TokenType;
+ int originalDepth = reader.CurrentDepth;
+ long originalBytesConsumed = reader.BytesConsumed;
- VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref state, ref reader);
+ OnReadEnumerable(ref state, ref reader);
+
+ VerifyRead(originalTokenType, originalDepth, originalBytesConsumed, ref reader);
+ }
}
public JsonClassInfo RuntimeClassInfo
public bool ShouldSerialize { get; private set; }
public bool ShouldDeserialize { get; private set; }
- private void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader)
+ private void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, ref Utf8JsonReader reader)
{
switch (tokenType)
{
case JsonTokenType.StartArray:
if (reader.TokenType != JsonTokenType.EndArray)
{
- ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterRead(ConverterBase);
}
else if (depth != reader.CurrentDepth)
{
- ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterRead(ConverterBase);
}
// Should not be possible to have not read anything.
case JsonTokenType.StartObject:
if (reader.TokenType != JsonTokenType.EndObject)
{
- ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterRead(ConverterBase);
}
else if (depth != reader.CurrentDepth)
{
- ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterRead(ConverterBase);
}
// Should not be possible to have not read anything.
// Reading a single property value.
if (reader.BytesConsumed != bytesConsumed)
{
- ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterRead(ConverterBase);
}
// Should not be possible to change token type.
JsonPropertyInfo propertyInfo = ElementClassInfo.PolicyProperty;
propertyInfo.WriteEnumerable(ref state, writer);
}
- else
+ // For performance on release build, don't verify converter correctness for internal converters.
+ else if (HasInternalConverter)
{
+#if DEBUG
int originalDepth = writer.CurrentDepth;
+#endif
OnWrite(ref state.Current, writer);
- if (originalDepth != writer.CurrentDepth)
- {
- ThrowHelper.ThrowJsonException_SerializationConverterWrite(state.PropertyPath(), ConverterBase.ToString());
- }
+#if DEBUG
+ VerifyWrite(originalDepth, writer);
+#endif
+ }
+ else
+ {
+ int originalDepth = writer.CurrentDepth;
+ OnWrite(ref state.Current, writer);
+ VerifyWrite(originalDepth, writer);
}
}
public void WriteDictionary(ref WriteStack state, Utf8JsonWriter writer)
{
Debug.Assert(ShouldSerialize);
- int originalDepth = writer.CurrentDepth;
- OnWriteDictionary(ref state.Current, writer);
+ // For performance on release build, don't verify converter correctness for internal converters.
+ if (HasInternalConverter)
+ {
+#if DEBUG
+ int originalDepth = writer.CurrentDepth;
+#endif
- if (originalDepth != writer.CurrentDepth)
+ OnWriteDictionary(ref state.Current, writer);
+
+#if DEBUG
+ VerifyWrite(originalDepth, writer);
+#endif
+ }
+ else
{
- ThrowHelper.ThrowJsonException_SerializationConverterWrite(state.PropertyPath(), ConverterBase.ToString());
+ int originalDepth = writer.CurrentDepth;
+ OnWriteDictionary(ref state.Current, writer);
+ VerifyWrite(originalDepth, writer);
}
}
public void WriteEnumerable(ref WriteStack state, Utf8JsonWriter writer)
{
Debug.Assert(ShouldSerialize);
- int originalDepth = writer.CurrentDepth;
- OnWriteEnumerable(ref state.Current, writer);
+ // For performance on release build, don't verify converter correctness for internal converters.
+ if (HasInternalConverter)
+ {
+#if DEBUG
+ int originalDepth = writer.CurrentDepth;
+#endif
+
+ OnWriteEnumerable(ref state.Current, writer);
+
+#if DEBUG
+ VerifyWrite(originalDepth, writer);
+#endif
+ }
+ else
+ {
+ int originalDepth = writer.CurrentDepth;
+ OnWriteEnumerable(ref state.Current, writer);
+ VerifyWrite(originalDepth, writer);
+ }
+ }
+ private void VerifyWrite(int originalDepth, Utf8JsonWriter writer)
+ {
if (originalDepth != writer.CurrentDepth)
{
- ThrowHelper.ThrowJsonException_SerializationConverterWrite(state.PropertyPath(), ConverterBase.ToString());
+ ThrowHelper.ThrowJsonException_SerializationConverterWrite(ConverterBase);
}
}
}
using System.Collections.Generic;
using System.Diagnostics;
-using System.Text.Json.Serialization;
namespace System.Text.Json
{
JsonPropertyInfoCommon<TClass, TDeclaredProperty, TRuntimeProperty, TConverter>
where TConverter : TDeclaredProperty
{
- protected override void OnRead(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnRead(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
TConverter value = Converter.Read(ref reader, RuntimePropertyType, Options);
}
}
- protected override void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnReadEnumerable(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
- if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible))
+ if (state.Current.KeyName == null && state.Current.IsProcessingDictionaryOrIDictionaryConstructible())
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
return;
}
// We need an initialized array in order to store the values.
- if (state.Current.IsProcessingEnumerable && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
+ if (state.Current.IsProcessingEnumerable() && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
return;
}
TConverter value = Converter.Read(ref reader, RuntimePropertyType, Options);
- JsonSerializer.ApplyValueToEnumerable(ref value, ref state, ref reader);
+ JsonSerializer.ApplyValueToEnumerable(ref value, ref state);
}
protected override void OnWrite(ref WriteStackFrame current, Utf8JsonWriter writer)
JsonPropertyInfoCommon<TClass, TDeclaredProperty, TRuntimeProperty, TConverter>
where TDeclaredProperty : TConverter
{
- protected override void OnRead(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnRead(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
TConverter value = Converter.Read(ref reader, RuntimePropertyType, Options);
return;
}
- protected override void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnReadEnumerable(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
- if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible))
+ if (state.Current.KeyName == null && state.Current.IsProcessingDictionaryOrIDictionaryConstructible())
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
return;
}
// We need an initialized array in order to store the values.
- if (state.Current.IsProcessingEnumerable && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
+ if (state.Current.IsProcessingEnumerable() && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
return;
}
TConverter value = Converter.Read(ref reader, RuntimePropertyType, Options);
- JsonSerializer.ApplyValueToEnumerable(ref value, ref state, ref reader);
+ JsonSerializer.ApplyValueToEnumerable(ref value, ref state);
}
protected override void OnWrite(ref WriteStackFrame current, Utf8JsonWriter writer)
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Text.Json.Serialization;
namespace System.Text.Json
{
{
private static readonly Type s_underlyingType = typeof(TProperty);
- protected override void OnRead(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnRead(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
TProperty value = Converter.Read(ref reader, s_underlyingType, Options);
}
}
- protected override void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader)
+ protected override void OnReadEnumerable(ref ReadStack state, ref Utf8JsonReader reader)
{
if (Converter == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType);
}
TProperty value = Converter.Read(ref reader, s_underlyingType, Options);
TProperty? nullableValue = new TProperty?(value);
- JsonSerializer.ApplyValueToEnumerable(ref nullableValue, ref state, ref reader);
+ JsonSerializer.ApplyValueToEnumerable(ref nullableValue, ref state);
}
protected override void OnWrite(ref WriteStackFrame current, Utf8JsonWriter writer)
Type arrayType = jsonPropertyInfo.RuntimePropertyType;
if (!typeof(IEnumerable).IsAssignableFrom(arrayType))
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(arrayType);
}
- Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary);
+ Debug.Assert(state.Current.IsProcessingCollection());
if (state.Current.CollectionPropertyInitialized)
{
state.Current.CollectionPropertyInitialized = true;
- if (state.Current.JsonClassInfo.ClassType == ClassType.Value)
- {
- // Custom converter code path.
- state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader);
- }
- else
- {
- // Set or replace the existing enumerable value.
- object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state);
+ // We should not be processing custom converters here.
+ Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Value);
+
+ // Set or replace the existing enumerable value.
+ object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state);
- // If value is not null, then we don't have a converter so apply the value.
- if (value != null)
+ // If value is not null, then we don't have a converter so apply the value.
+ if (value != null)
+ {
+ if (state.Current.ReturnValue != null)
{
- if (state.Current.ReturnValue != null)
- {
- state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
- }
- else
- {
- // Primitive arrays being returned without object
- state.Current.SetReturnValue(value);
- }
+ state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
+ }
+ else
+ {
+ // Primitive arrays being returned without object
+ state.Current.SetReturnValue(value);
}
}
}
private static bool HandleEndArray(
JsonSerializerOptions options,
- ref Utf8JsonReader reader,
ref ReadStack state)
{
bool lastFrame = state.IsLastFrame;
value = converter.CreateFromList(ref state, (IList)value, options);
state.Current.TempEnumerableValues = null;
}
- else if (state.Current.IsEnumerableProperty)
+ else if (state.Current.IsProcessingProperty(ClassType.Enumerable))
{
// We added the items to the list already.
state.Current.EndProperty();
state.Current.ReturnValue = value;
return true;
}
- else if (state.Current.IsEnumerable || state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
+ else if (state.Current.IsProcessingCollectionObject())
{
// Returning a non-converted list.
return true;
}
// else there must be an outer object, so we'll return false here.
}
- else if (state.Current.IsEnumerable)
+ else if (state.Current.IsProcessingObject(ClassType.Enumerable))
{
state.Pop();
}
- ApplyObjectToEnumerable(value, ref state, ref reader);
+ ApplyObjectToEnumerable(value, ref state);
return false;
}
internal static void ApplyObjectToEnumerable(
object value,
ref ReadStack state,
- ref Utf8JsonReader reader,
bool setPropertyDirectly = false)
{
Debug.Assert(!state.Current.SkipProperty);
- if (state.Current.IsEnumerable)
+ if (state.Current.IsProcessingObject(ClassType.Enumerable))
{
if (state.Current.TempEnumerableValues != null)
{
{
if (!(state.Current.ReturnValue is IList list))
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType(), reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(value.GetType());
return;
}
list.Add(value);
}
}
- else if (!setPropertyDirectly && state.Current.IsEnumerableProperty)
+ else if (!setPropertyDirectly && state.Current.IsProcessingProperty(ClassType.Enumerable))
{
Debug.Assert(state.Current.JsonPropertyInfo != null);
Debug.Assert(state.Current.ReturnValue != null);
}
}
}
- else if (state.Current.IsDictionary || (state.Current.IsDictionaryProperty && !setPropertyDirectly))
+ else if (state.Current.IsProcessingObject(ClassType.Dictionary) ||
+ (state.Current.IsProcessingProperty(ClassType.Dictionary) && !setPropertyDirectly))
{
Debug.Assert(state.Current.ReturnValue != null);
IDictionary dictionary = (IDictionary)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
Debug.Assert(!string.IsNullOrEmpty(key));
dictionary[key] = value;
}
- else if (state.Current.IsIDictionaryConstructible ||
- (state.Current.IsIDictionaryConstructibleProperty && !setPropertyDirectly))
+ else if (state.Current.IsProcessingObject(ClassType.IDictionaryConstructible) ||
+ (state.Current.IsProcessingProperty(ClassType.IDictionaryConstructible) && !setPropertyDirectly))
{
Debug.Assert(state.Current.TempDictionaryValues != null);
IDictionary dictionary = (IDictionary)state.Current.TempDictionaryValues;
// If this method is changed, also change ApplyObjectToEnumerable.
internal static void ApplyValueToEnumerable<TProperty>(
ref TProperty value,
- ref ReadStack state,
- ref Utf8JsonReader reader)
+ ref ReadStack state)
{
Debug.Assert(!state.Current.SkipProperty);
- if (state.Current.IsEnumerable)
+ if (state.Current.IsProcessingObject(ClassType.Enumerable))
{
if (state.Current.TempEnumerableValues != null)
{
((IList<TProperty>)state.Current.ReturnValue).Add(value);
}
}
- else if (state.Current.IsEnumerableProperty)
+ else if (state.Current.IsProcessingProperty(ClassType.Enumerable))
{
Debug.Assert(state.Current.JsonPropertyInfo != null);
Debug.Assert(state.Current.ReturnValue != null);
}
}
}
- else if (state.Current.IsProcessingDictionary)
+ else if (state.Current.IsProcessingDictionary())
{
Debug.Assert(state.Current.ReturnValue != null);
IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue);
Debug.Assert(!string.IsNullOrEmpty(key));
dictionary[key] = value;
}
- else if (state.Current.IsProcessingIDictionaryConstructible)
+ else if (state.Current.IsProcessingIDictionaryConstructible())
{
Debug.Assert(state.Current.TempDictionaryValues != null);
IDictionary<string, TProperty> dictionary = (IDictionary<string, TProperty>)state.Current.TempDictionaryValues;
{
public static partial class JsonSerializer
{
- private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
+ private static void HandleStartDictionary(JsonSerializerOptions options, ref ReadStack state)
{
- Debug.Assert(!state.Current.IsProcessingEnumerable);
+ Debug.Assert(!state.Current.IsProcessingEnumerable());
JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (jsonPropertyInfo == null)
jsonPropertyInfo.ElementClassInfo.Type != typeof(object) &&
jsonPropertyInfo.ElementClassInfo.Type != typeof(JsonElement))
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type);
}
JsonClassInfo classInfo = state.Current.JsonClassInfo;
- if (state.Current.IsProcessingIDictionaryConstructible)
+ if (state.Current.IsProcessingIDictionaryConstructible())
{
state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateConcreteDictionary();
}
{
if (classInfo.CreateObject == null)
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type);
return;
}
state.Current.ReturnValue = classInfo.CreateObject();
state.Current.CollectionPropertyInitialized = true;
- if (state.Current.IsProcessingIDictionaryConstructible)
+ if (state.Current.IsProcessingIDictionaryConstructible())
{
JsonClassInfo dictionaryClassInfo;
if (jsonPropertyInfo.DeclaredPropertyType == jsonPropertyInfo.ImplementedPropertyType)
}
}
- private static void HandleEndDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
+ private static void HandleEndDictionary(JsonSerializerOptions options, ref ReadStack state)
{
- if (state.Current.SkipProperty)
- {
- // Todo: determine if this is reachable.
- return;
- }
+ Debug.Assert(!state.Current.SkipProperty);
- if (state.Current.IsDictionaryProperty)
+ if (state.Current.IsProcessingProperty(ClassType.Dictionary))
{
// Handle special case of DataExtensionProperty where we just added a dictionary element to the extension property.
// Since the JSON value is not a dictionary element (it's a normal property in JSON) a JsonTokenType.EndObject
// encountered here is from the outer object so forward to HandleEndObject().
if (state.Current.JsonClassInfo.DataExtensionProperty == state.Current.JsonPropertyInfo)
{
- HandleEndObject(ref reader, ref state);
+ HandleEndObject(ref state);
}
else
{
state.Current.EndProperty();
}
}
- else if (state.Current.IsIDictionaryConstructibleProperty)
+ else if (state.Current.IsProcessingProperty(ClassType.IDictionaryConstructible))
{
Debug.Assert(state.Current.TempDictionaryValues != null);
JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
else
{
state.Pop();
- ApplyObjectToEnumerable(value, ref state, ref reader);
+ ApplyObjectToEnumerable(value, ref state);
}
}
}
Debug.Assert(jsonPropertyInfo != null);
- if (state.Current.IsCollectionForClass)
+ if (state.Current.IsProcessingCollectionObject())
{
AddNullToCollection(jsonPropertyInfo, ref reader, ref state);
return false;
}
- if (state.Current.IsCollectionForProperty)
+ if (state.Current.IsProcessingCollectionProperty())
{
if (state.Current.CollectionPropertyInitialized)
{
else
{
// Set the property to null.
- ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: true);
+ ApplyObjectToEnumerable(null, ref state, setPropertyDirectly: true);
// Reset so that `Is*Property` no longer returns true
state.Current.EndProperty();
else
{
// Assume collection types are reference types and can have null assigned.
- ApplyObjectToEnumerable(null, ref state, ref reader);
+ ApplyObjectToEnumerable(null, ref state);
}
}
}
{
private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state)
{
- Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingIDictionaryConstructible);
+ Debug.Assert(!state.Current.IsProcessingDictionaryOrIDictionaryConstructible());
- if (state.Current.IsProcessingEnumerable)
+ if (state.Current.IsProcessingEnumerable())
{
// A nested object within an enumerable.
Type objType = state.Current.GetElementType();
}
}
- if (state.Current.IsProcessingIDictionaryConstructible)
+ if (state.Current.IsProcessingIDictionaryConstructible())
{
state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateConcreteDictionary();
}
}
}
- private static void HandleEndObject(ref Utf8JsonReader reader, ref ReadStack state)
+ private static void HandleEndObject(ref ReadStack state)
{
// Only allow dictionaries to be processed here if this is the DataExtensionProperty.
Debug.Assert(
- (!state.Current.IsProcessingDictionary || state.Current.JsonClassInfo.DataExtensionProperty == state.Current.JsonPropertyInfo) &&
- !state.Current.IsProcessingIDictionaryConstructible);
+ (!state.Current.IsProcessingDictionary() || state.Current.JsonClassInfo.DataExtensionProperty == state.Current.JsonPropertyInfo) &&
+ !state.Current.IsProcessingIDictionaryConstructible());
// Check if we are trying to build the sorted cache.
if (state.Current.PropertyRefCache != null)
else
{
state.Pop();
- ApplyObjectToEnumerable(value, ref state, ref reader);
+ ApplyObjectToEnumerable(value, ref state);
}
}
}
Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default);
Debug.Assert(state.Current.JsonClassInfo != default);
- if ((state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible) &&
+ if (state.Current.IsProcessingDictionaryOrIDictionaryConstructible() &&
state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo)
{
- if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible)
+ if (state.Current.IsProcessingObject(ClassType.Dictionary | ClassType.IDictionaryConstructible))
{
state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty;
}
- Debug.Assert(
- state.Current.IsDictionary ||
- (state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null) ||
- state.Current.IsIDictionaryConstructible ||
- (state.Current.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null));
-
state.Current.KeyName = reader.GetString();
}
else
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers;
+
namespace System.Text.Json
{
public static partial class JsonSerializer
{
+ // The maximum length of a byte array before we ask for the actual transcoded byte size.
+ // The "int.MaxValue / 2" is used because max byte[] length is a bit less than int.MaxValue
+ // and because we don't want to allocate such a large buffer if not necessary.
+ private const int MaxArrayLengthBeforeCalculatingSize = int.MaxValue / 2 / JsonConstants.MaxExpansionFactorWhileTranscoding;
+
/// <summary>
/// Parse the text representing a single JSON value into a <typeparamref name="TValue"/>.
/// </summary>
/// </remarks>
public static TValue Deserialize<TValue>(string json, JsonSerializerOptions options = null)
{
- if (json == null)
- throw new ArgumentNullException(nameof(json));
-
- return (TValue)ParseCore(json, typeof(TValue), options);
+ return (TValue)Deserialize(json, typeof(TValue), options);
}
/// <summary>
public static object Deserialize(string json, Type returnType, JsonSerializerOptions options = null)
{
if (json == null)
+ {
throw new ArgumentNullException(nameof(json));
+ }
if (returnType == null)
+ {
throw new ArgumentNullException(nameof(returnType));
+ }
- return ParseCore(json, returnType, options);
- }
-
- private static object ParseCore(string json, Type returnType, JsonSerializerOptions options = null)
- {
if (options == null)
{
options = JsonSerializerOptions.s_defaultOptions;
}
- // todo: use an array pool here for smaller requests to avoid the alloc?
- byte[] jsonBytes = JsonReaderHelper.s_utf8Encoding.GetBytes(json);
- var readerState = new JsonReaderState(options.GetReaderOptions());
- var reader = new Utf8JsonReader(jsonBytes, isFinalBlock: true, readerState);
- object result = ReadCore(returnType, options, ref reader);
+ object result;
+ byte[] tempArray = null;
- if (reader.BytesConsumed != jsonBytes.Length)
+ int maxBytes;
+
+ // For performance, avoid asking for the actual byte count unless necessary.
+ if (json.Length > MaxArrayLengthBeforeCalculatingSize)
+ {
+ // Get the actual byte count in order to handle large input.
+ maxBytes = JsonReaderHelper.GetUtf8ByteCount(json.AsSpan());
+ }
+ else
+ {
+ maxBytes = json.Length * JsonConstants.MaxExpansionFactorWhileTranscoding;
+ }
+
+ Span<byte> utf8 = maxBytes <= JsonConstants.StackallocThreshold ?
+ stackalloc byte[maxBytes] :
+ (tempArray = ArrayPool<byte>.Shared.Rent(maxBytes));
+
+ try
+ {
+ int actualByteCount = JsonReaderHelper.GetUtf8FromText(json.AsSpan(), utf8);
+ utf8 = utf8.Slice(0, actualByteCount);
+
+ var readerState = new JsonReaderState(options.GetReaderOptions());
+ var reader = new Utf8JsonReader(utf8, isFinalBlock: true, readerState);
+ result = ReadCore(returnType, options, ref reader);
+
+ if (reader.BytesConsumed != actualByteCount)
+ {
+ ThrowHelper.ThrowJsonException_DeserializeDataRemaining(
+ actualByteCount, actualByteCount - reader.BytesConsumed);
+ }
+ }
+ finally
{
- ThrowHelper.ThrowJsonException_DeserializeDataRemaining(
- jsonBytes.Length, jsonBytes.Length - reader.BytesConsumed);
+ if (tempArray != null)
+ {
+ utf8.Clear();
+ ArrayPool<byte>.Shared.Return(tempArray);
+ }
}
return result;
break;
}
}
- else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
+ else if (readStack.Current.IsProcessingDictionaryOrIDictionaryConstructible())
{
- HandleStartDictionary(options, ref reader, ref readStack);
+ HandleStartDictionary(options, ref readStack);
}
else
{
// A non-dictionary property can also have EndProperty() called when completed, although it is redundant.
readStack.Current.EndProperty();
}
- else if (readStack.Current.IsProcessingDictionary || readStack.Current.IsProcessingIDictionaryConstructible)
+ else if (readStack.Current.IsProcessingDictionaryOrIDictionaryConstructible())
{
- HandleEndDictionary(options, ref reader, ref readStack);
+ HandleEndDictionary(options, ref readStack);
}
else
{
- HandleEndObject(ref reader, ref readStack);
+ HandleEndObject(ref readStack);
}
}
else if (tokenType == JsonTokenType.StartArray)
}
else if (tokenType == JsonTokenType.EndArray)
{
- HandleEndArray(options, ref reader, ref readStack);
+ HandleEndArray(options, ref readStack);
}
else if (tokenType == JsonTokenType.Null)
{
}
readStack.BytesConsumed += reader.BytesConsumed;
- return;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static partial class JsonSerializer
{
// There are three conditions to consider for an object (primitive value, enumerable or object) being processed here:
- // 1) The object type was specified as the root-level return type to a Parse\Read method.
- // 2) The object is property on a parent object.
+ // 1) The object type was specified as the root-level return type to a Deserialize method.
+ // 2) The object is a property on a parent object.
// 3) The object is an element in an enumerable.
private static bool Write(
Utf8JsonWriter writer,
{
do
{
- WriteStackFrame current = state.Current;
- switch (current.JsonClassInfo.ClassType)
+ switch (state.Current.JsonClassInfo.ClassType)
{
case ClassType.Enumerable:
- finishedSerializing = HandleEnumerable(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
+ finishedSerializing = HandleEnumerable(state.Current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
break;
case ClassType.Value:
- Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value);
- current.JsonPropertyInfo.Write(ref state, writer);
+ Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Value);
+ state.Current.JsonPropertyInfo.Write(ref state, writer);
finishedSerializing = true;
break;
case ClassType.Dictionary:
case ClassType.IDictionaryConstructible:
- finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
+ finishedSerializing = HandleDictionary(state.Current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
break;
default:
Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object ||
if (frame.JsonClassInfo != null)
{
- if (frame.IsProcessingDictionary)
+ if (frame.IsProcessingDictionary())
{
// For dictionaries add the key.
AppendPropertyName(sb, frame.KeyName);
}
- else if (frame.IsProcessingEnumerable)
+ else if (frame.IsProcessingEnumerable())
{
// For enumerables add the index.
IList list = frame.TempEnumerableValues;
// Has an array or dictionary property been initialized.
public bool CollectionPropertyInitialized;
+ // The current JSON data for a property does not match a given POCO, so ignore the property (recursively).
+ public bool Drain;
+
// Support IDictionary constructible types, i.e. types that we
// support by passing and IDictionary to their constructors:
// immutable dictionaries, Hashtable, SortedList
public int PropertyIndex;
public List<PropertyRef> PropertyRefCache;
- // The current JSON data for a property does not match a given POCO, so ignore the property (recursively).
- public bool Drain;
+ /// <summary>
+ /// Is the current object an Enumerable, Dictionary or IDictionaryConstructible.
+ /// </summary>
+ public bool IsProcessingCollectionObject()
+ {
+ return IsProcessingObject(ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible);
+ }
- public bool IsCollectionForClass => IsEnumerable || IsDictionary || IsIDictionaryConstructible;
- public bool IsCollectionForProperty => IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty;
+ /// <summary>
+ /// Is the current property an Enumerable, Dictionary or IDictionaryConstructible.
+ /// </summary>
+ public bool IsProcessingCollectionProperty()
+ {
+ return IsProcessingProperty(ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible);
+ }
- public bool IsIDictionaryConstructible => JsonClassInfo.ClassType == ClassType.IDictionaryConstructible;
- public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary;
+ /// <summary>
+ /// Is the current object or property an Enumerable, Dictionary or IDictionaryConstructible.
+ /// </summary>
+ public bool IsProcessingCollection()
+ {
+ return IsProcessingObject(ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible) ||
+ IsProcessingProperty(ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible);
+ }
- public bool IsDictionaryProperty => JsonPropertyInfo != null &&
- !JsonPropertyInfo.IsPropertyPolicy &&
- JsonPropertyInfo.ClassType == ClassType.Dictionary;
- public bool IsIDictionaryConstructibleProperty => JsonPropertyInfo != null &&
- !JsonPropertyInfo.IsPropertyPolicy && (JsonPropertyInfo.ClassType == ClassType.IDictionaryConstructible);
+ /// <summary>
+ /// Is the current object or property a Dictionary.
+ /// </summary>
+ public bool IsProcessingDictionary()
+ {
+ return IsProcessingObject(ClassType.Dictionary) ||
+ IsProcessingProperty(ClassType.Dictionary);
+ }
- public bool IsEnumerable => JsonClassInfo.ClassType == ClassType.Enumerable;
+ /// <summary>
+ /// Is the current object or property an IDictionaryConstructible.
+ /// </summary>
+ public bool IsProcessingIDictionaryConstructible()
+ {
+ return IsProcessingObject(ClassType.IDictionaryConstructible)
+ || IsProcessingProperty(ClassType.IDictionaryConstructible);
+ }
- public bool IsEnumerableProperty =>
- JsonPropertyInfo != null &&
- !JsonPropertyInfo.IsPropertyPolicy &&
- JsonPropertyInfo.ClassType == ClassType.Enumerable;
+ /// <summary>
+ /// Is the current object or property a Dictionary or IDictionaryConstructible.
+ /// </summary>
+ public bool IsProcessingDictionaryOrIDictionaryConstructible()
+ {
+ return IsProcessingObject(ClassType.Dictionary | ClassType.IDictionaryConstructible) ||
+ IsProcessingProperty(ClassType.Dictionary | ClassType.IDictionaryConstructible);
+ }
- public bool IsProcessingEnumerableOrDictionary => IsProcessingEnumerable || IsProcessingDictionary || IsProcessingIDictionaryConstructible;
- public bool IsProcessingDictionary => IsDictionary || IsDictionaryProperty;
- public bool IsProcessingIDictionaryConstructible => IsIDictionaryConstructible || IsIDictionaryConstructibleProperty;
- public bool IsProcessingEnumerable => IsEnumerable || IsEnumerableProperty;
+ /// <summary>
+ /// Is the current object or property an Enumerable.
+ /// </summary>
+ public bool IsProcessingEnumerable()
+ {
+ return IsProcessingObject(ClassType.Enumerable) ||
+ IsProcessingProperty(ClassType.Enumerable);
+ }
+ /// <summary>
+ /// Is the current object of the provided <paramref name="classTypes"/>.
+ /// </summary>
+ public bool IsProcessingObject(ClassType classTypes)
+ {
+ return (JsonClassInfo.ClassType & classTypes) != 0;
+ }
+
+ /// <summary>
+ /// Is the current property of the provided <paramref name="classTypes"/>.
+ /// </summary>
+ public bool IsProcessingProperty(ClassType classTypes)
+ {
+ return JsonPropertyInfo != null &&
+ !JsonPropertyInfo.IsPropertyPolicy &&
+ (JsonPropertyInfo.ClassType & classTypes) != 0;
+ }
+
+ /// <summary>
+ /// Determine whether a StartObject or StartArray token should be treated as a value.
+ /// This allows read-ahead functionality required for Streams so that a custom converter
+ /// does not run out of data and fail.
+ /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- // Determine whether a StartObject or StartArray token should be treated as a value.
public bool IsProcessingValue()
{
if (SkipProperty)
classType = JsonPropertyInfo.ClassType;
}
- return classType == ClassType.Value || classType == ClassType.Unknown;
+ // A ClassType.Value indicates the object has a converter and ClassType.Unknown indicates the
+ // property or element type is System.Object.
+ // System.Object is treated as a JsonElement which requires returning true from this
+ // method in order to properly read-ahead (since JsonElement has a custom converter).
+ return (classType & (ClassType.Value | ClassType.Unknown)) != 0;
}
public void Initialize(Type type, JsonSerializerOptions options)
public void InitializeJsonPropertyInfo()
{
- if (JsonClassInfo.ClassType == ClassType.Value ||
- JsonClassInfo.ClassType == ClassType.Enumerable ||
- JsonClassInfo.ClassType == ClassType.Dictionary ||
- JsonClassInfo.ClassType == ClassType.IDictionaryConstructible)
+ if (IsProcessingObject(ClassType.Value | ClassType.Enumerable | ClassType.Dictionary | ClassType.IDictionaryConstructible))
{
JsonPropertyInfo = JsonClassInfo.PolicyProperty;
}
}
else
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.DeclaredPropertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(jsonPropertyInfo.DeclaredPropertyType);
return null;
}
}
else
{
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propertyType, reader, state.JsonPath());
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propertyType);
return null;
}
}
public Type GetElementType()
{
- if (IsCollectionForProperty)
+ if (IsProcessingCollectionProperty())
{
return JsonPropertyInfo.ElementClassInfo.Type;
}
- if (IsCollectionForClass)
+ if (IsProcessingCollectionObject())
{
return JsonClassInfo.ElementClassInfo.Type;
}
public static IEnumerable GetEnumerableValue(ref ReadStackFrame current)
{
- if (current.IsEnumerable)
+ if (current.IsProcessingObject(ClassType.Enumerable))
{
if (current.ReturnValue != null)
{
}
public bool SkipProperty => Drain ||
- ReferenceEquals(JsonPropertyInfo, JsonPropertyInfo.s_missingProperty) ||
- (JsonPropertyInfo?.IsPropertyPolicy == false && JsonPropertyInfo?.ShouldDeserialize == false);
+ JsonPropertyInfo != null &&
+ JsonPropertyInfo.IsPropertyPolicy == false &&
+ JsonPropertyInfo.ShouldDeserialize == false;
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType, in Utf8JsonReader reader, string path, Exception innerException = null)
+ public static void ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType)
{
- ThrowJsonException(SR.Format(SR.DeserializeUnableToConvertValue, propertyType), in reader, path, innerException);
+ var ex = new JsonException(SR.Format(SR.DeserializeUnableToConvertValue, propertyType));
+ ex.AppendPathInformation = true;
+ throw ex;
}
[MethodImpl(MethodImplOptions.NoInlining)]
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowJsonException_DepthTooLarge(int currentDepth, in WriteStack writeStack, JsonSerializerOptions options)
+ public static void ThrowJsonException_DepthTooLarge(int currentDepth, JsonSerializerOptions options)
{
- var ex = new JsonException(SR.Format(SR.DepthTooLarge, currentDepth, options.EffectiveMaxDepth));
- AddExceptionInformation(writeStack, ex);
- throw ex;
+ throw new JsonException(SR.Format(SR.DepthTooLarge, currentDepth, options.EffectiveMaxDepth));
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowJsonException_SerializationConverterRead(in Utf8JsonReader reader, string path, string converter)
+ public static void ThrowJsonException_SerializationConverterRead(JsonConverter converter)
{
- ThrowJsonException(SR.Format(SR.SerializationConverterRead, converter), reader, path);
+ var ex = new JsonException(SR.Format(SR.SerializationConverterRead, converter));
+ ex.AppendPathInformation = true;
+ throw ex;
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public static void ThrowJsonException_SerializationConverterWrite(string path, string converter)
+ public static void ThrowJsonException_SerializationConverterWrite(JsonConverter converter)
{
- ThrowJsonException(SR.Format(SR.SerializationConverterWrite, converter), path);
+ var ex = new JsonException(SR.Format(SR.SerializationConverterWrite, converter));
+ ex.AppendPathInformation = true;
+ throw ex;
}
[MethodImpl(MethodImplOptions.NoInlining)]
throw new JsonException(SR.Format(SR.DeserializeDataRemaining, length, bytesRemaining), path: null, lineNumber: null, bytePositionInLine: null);
}
- // todo: since we now catch and re-throw JsonException and add Path etc, we can clean up callers to this to not pass the reader and path.
- private static void ThrowJsonException(string message, in Utf8JsonReader reader, string path, Exception innerException = null)
- {
- long lineNumber = reader.CurrentState._lineNumber;
- long bytePositionInLine = reader.CurrentState._bytePositionInLine;
-
- message += $" Path: {path} | LineNumber: {lineNumber} | BytePositionInLine: {bytePositionInLine}.";
- throw new JsonException(message, path, lineNumber, bytePositionInLine, innerException);
- }
-
- private static void ThrowJsonException(string message, string path, Exception innerException = null)
- {
- message += $" Path: {path}.";
- throw new JsonException(message, path, null, null, innerException);
- }
-
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ReThrowWithPath(in ReadStack readStack, JsonReaderException ex)
{
string path = readStack.JsonPath();
ex.Path = path;
- // If the message is empty, use a default message with Path, LineNumber and BytePositionInLine.
- if (string.IsNullOrEmpty(ex.Message))
+ string message = ex.Message;
+
+ // If the message is empty or contains EmbedPathInfoFlag then append the Path, LineNumber and BytePositionInLine.
+ if (string.IsNullOrEmpty(message))
{
+ // Use a default message.
Type propertyType = readStack.Current.JsonPropertyInfo?.RuntimePropertyType;
if (propertyType == null)
{
propertyType = readStack.Current.JsonClassInfo.Type;
}
- ex.SetMessage($"{SR.Format(SR.DeserializeUnableToConvertValue, propertyType)} Path: {path} | LineNumber: {lineNumber} | BytePositionInLine: {bytePositionInLine}.");
+ message = SR.Format(SR.DeserializeUnableToConvertValue, propertyType);
+ ex.AppendPathInformation = true;
+ }
+
+ if (ex.AppendPathInformation)
+ {
+ message += $" Path: {path} | LineNumber: {lineNumber} | BytePositionInLine: {bytePositionInLine}.";
+ ex.SetMessage(message);
}
}
ex.Path = path;
// If the message is empty, use a default message with the Path.
- if (string.IsNullOrEmpty(ex.Message))
+ string message = ex.Message;
+ if (string.IsNullOrEmpty(message))
{
- ex.SetMessage(SR.Format(SR.SerializeUnableToSerialize, path));
+ message = SR.Format(SR.SerializeUnableToSerialize);
+ ex.AppendPathInformation = true;
+ }
+
+ if (ex.AppendPathInformation)
+ {
+ message += $" Path: {path}.";
+ ex.SetMessage(message);
}
}
throw new InvalidOperationException(SR.Format(SR.SerializationDuplicateTypeAttribute, classType, attribute));
}
-
-
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowInvalidOperationException_SerializationDataExtensionPropertyInvalid(JsonClassInfo jsonClassInfo, JsonPropertyInfo jsonPropertyInfo)
{
{
public static partial class ValueTests
{
+ public static bool IsX64 { get; } = Environment.Is64BitProcess;
+
[Fact]
public static void ReadPrimitives()
{
Assert.Equal(netcoreExpectedValue, testCode());
}
}
+
+ // NOTE: LongInputString test is constrained to run on Windows and MacOSX because it causes
+ // problems on Linux due to the way deferred memory allocation works. On Linux, the allocation can
+ // succeed even if there is not enough memory but then the test may get killed by the OOM killer at the
+ // time the memory is accessed which triggers the full memory allocation.
+ private const int MaxExpansionFactorWhileTranscoding = 3;
+ private const int MaxArrayLengthBeforeCalculatingSize = int.MaxValue / 2 / MaxExpansionFactorWhileTranscoding;
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)]
+ [ConditionalTheory(nameof(IsX64))]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize - 3)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize - 2)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize - 1)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize + 1)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize + 2)]
+ [InlineData(MaxArrayLengthBeforeCalculatingSize + 3)]
+ [OuterLoop]
+ public static void LongInputString(int length)
+ {
+ // Verify boundary conditions in Deserialize() that inspect the size to determine allocation strategy.
+ string repeated = new string('x', length - 2);
+ string json = $"\"{repeated}\"";
+ Assert.Equal(length, json.Length);
+
+ string str = JsonSerializer.Deserialize<string>(json);
+ Assert.Equal(repeated, str);
+ }
}
}