// The limit to how many property names from the JSON are cached in _propertyRefsSorted before using PropertyCache.
private const int PropertyNameCountCacheThreshold = 64;
- // All of the serializable properties on a POCO keyed on property name.
+ // All of the serializable properties on a POCO (except the optional extension property) keyed on property name.
public volatile Dictionary<string, JsonPropertyInfo> PropertyCache;
+ // All of the serializable properties on a POCO including the optional extension property.
+ // Used for performance during serialization instead of 'PropertyCache' above.
+ public volatile JsonPropertyInfo[] PropertyCacheArray;
+
// Fast cache of properties by first JSON ordering; may not contain all properties. Accessed before PropertyCache.
// Use an array (instead of List<T>) for highest performance.
private volatile PropertyRef[] _propertyRefsSorted;
}
}
+ JsonPropertyInfo[] cacheArray;
if (DetermineExtensionDataProperty(cache))
{
// Remove from cache since it is handled independently.
cache.Remove(DataExtensionProperty.NameAsString);
+
+ cacheArray = new JsonPropertyInfo[cache.Count + 1];
+
+ // Set the last element to the extension property.
+ cacheArray[cache.Count] = DataExtensionProperty;
+ }
+ else
+ {
+ cacheArray = new JsonPropertyInfo[cache.Count];
}
- // Set as a unit to avoid concurrency issues.
+ // Set fields when finished to avoid concurrency issues.
PropertyCache = cache;
+ cache.Values.CopyTo(cacheArray, 0);
+ PropertyCacheArray = cacheArray;
}
break;
case ClassType.Enumerable:
// Add a single property that maps to the class type so we can have policies applied.
AddPolicyProperty(type, options);
PropertyCache = new Dictionary<string, JsonPropertyInfo>();
+ PropertyCacheArray = Array.Empty<JsonPropertyInfo>();
break;
default:
Debug.Fail($"Unexpected class type: {ClassType}");
public JsonDictionaryConverter DictionaryConverter { get; private set; }
// The escaped name passed to the writer.
- public JsonEncodedText? EscapedName { get; private set; }
+ // Use a field here (not a property) to avoid value semantics.
+ public JsonEncodedText? EscapedName;
public static TAttribute GetAttribute<TAttribute>(PropertyInfo propertyInfo) where TAttribute : Attribute
{
// 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.Collections.Generic;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
namespace System.Text.Json
{
public static partial class JsonSerializer
{
+ // AggressiveInlining used although a large method it is only called from one location and is on a hot path.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool WriteObject(
JsonSerializerOptions options,
Utf8JsonWriter writer,
}
state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options);
- state.Current.PropertyEnumerator = state.Current.JsonClassInfo.PropertyCache.GetEnumerator();
state.Current.PropertyEnumeratorActive = true;
- state.Current.NextProperty();
+ state.Current.MoveToNextProperty = true;
}
- else if (state.Current.MoveToNextProperty)
+
+ if (state.Current.MoveToNextProperty)
{
state.Current.NextProperty();
}
// Determine if we are done enumerating properties.
- // If the ClassType is unknown, there will be a policy property applied
- JsonClassInfo classInfo = state.Current.JsonClassInfo;
- if (classInfo.ClassType != ClassType.Unknown && state.Current.PropertyEnumeratorActive)
+ if (state.Current.PropertyEnumeratorActive)
{
- HandleObject(state.Current.PropertyEnumerator.Current.Value, options, writer, ref state);
- return false;
- }
+ // If ClassType.Unknown at this point, we are typeof(object) which should not have any properties.
+ Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Unknown);
- if (state.Current.ExtensionDataStatus == Serialization.ExtensionDataWriteStatus.Writing)
- {
- JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.DataExtensionProperty;
- if (jsonPropertyInfo != null)
- {
- HandleObject(jsonPropertyInfo, options, writer, ref state);
- return false;
- }
+ JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.PropertyCacheArray[state.Current.PropertyEnumeratorIndex - 1];
+ HandleObject(jsonPropertyInfo, options, writer, ref state);
+
+ return false;
}
writer.WriteEndObject();
return true;
}
- private static bool HandleObject(
- JsonPropertyInfo jsonPropertyInfo,
- JsonSerializerOptions options,
- Utf8JsonWriter writer,
- ref WriteStack state)
+ // AggressiveInlining used although a large method it is only called from one location and is on a hot path.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void HandleObject(
+ JsonPropertyInfo jsonPropertyInfo,
+ JsonSerializerOptions options,
+ Utf8JsonWriter writer,
+ ref WriteStack state)
{
Debug.Assert(
state.Current.JsonClassInfo.ClassType == ClassType.Object ||
if (!jsonPropertyInfo.ShouldSerialize)
{
state.Current.MoveToNextProperty = true;
- return true;
+ return;
}
bool obtainedValue = false;
{
jsonPropertyInfo.Write(ref state, writer);
state.Current.MoveToNextProperty = true;
- return true;
+ return;
}
// A property that returns an enumerator keeps the same stack frame.
state.Current.MoveToNextProperty = true;
}
- return endOfEnumerable;
+ return;
}
// A property that returns a dictionary keeps the same stack frame.
state.Current.MoveToNextProperty = true;
}
- return endOfEnumerable;
+ return;
}
// A property that returns a type that is deserialized by passing an
state.Current.MoveToNextProperty = true;
}
- return endOfEnumerable;
+ return;
}
// A property that returns an object.
state.Current.MoveToNextProperty = true;
}
-
- return true;
}
}
}
return result;
}
- private static string WriteValueCore(Utf8JsonWriter writer, object value, Type type, JsonSerializerOptions options)
+ private static void WriteValueCore(Utf8JsonWriter writer, object value, Type type, JsonSerializerOptions options)
{
if (options == null)
{
options = JsonSerializerOptions.s_defaultOptions;
}
- string result;
-
- using (var output = new PooledByteBufferWriter(options.DefaultBufferSize))
- {
- WriteCore(writer, output, value, type, options);
- result = JsonReaderHelper.TranscodeHelper(output.WrittenMemory.Span);
- }
-
- return result;
+ WriteCore(writer, value, type, options);
}
private static void WriteCore(PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
{
- using var writer = new Utf8JsonWriter(output, options.GetWriterOptions());
- WriteCore(writer, output, value, type, options);
+ using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions()))
+ {
+ WriteCore(writer, value, type, options);
+ }
}
- private static void WriteCore(Utf8JsonWriter writer, PooledByteBufferWriter output, object value, Type type, JsonSerializerOptions options)
+ private static void WriteCore(Utf8JsonWriter writer, object value, Type type, JsonSerializerOptions options)
{
Debug.Assert(type != null || value == null);
/// </remarks>
public static string Serialize<TValue>(TValue value, JsonSerializerOptions options = null)
{
- return ToStringInternal(value, typeof(TValue), options);
+ return WriteCoreString(value, typeof(TValue), options);
}
/// <summary>
public static string Serialize(object value, Type inputType, JsonSerializerOptions options = null)
{
VerifyValueAndType(value, inputType);
-
- return ToStringInternal(value, inputType, options);
- }
-
- private static string ToStringInternal(object value, Type inputType, JsonSerializerOptions options)
- {
return WriteCoreString(value, inputType, options);
}
}
current.JsonPropertyInfo.Write(ref state, writer);
finishedSerializing = true;
break;
- case ClassType.Object:
- finishedSerializing = WriteObject(options, writer, ref state);
- break;
case ClassType.Dictionary:
case ClassType.IDictionaryConstructible:
finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state);
break;
default:
- Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Unknown);
+ Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object ||
+ state.Current.JsonClassInfo.ClassType == ClassType.Unknown);
- // Treat typeof(object) as an empty object.
finishedSerializing = WriteObject(options, writer, ref state);
break;
}
if (finishedSerializing)
{
- if (writer.CurrentDepth == 0 || writer.CurrentDepth == originalWriterDepth)
+ if (writer.CurrentDepth == originalWriterDepth)
{
break;
}
ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected(options.MaxDepth);
}
- // If serialization is not yet end and we surpass beyond flush threshold return false and flush stream.
+ // If serialization is not finished and we surpass flush threshold then return false which will flush stream.
if (flushThreshold >= 0 && writer.BytesPending > flushThreshold)
{
return false;
// See the LICENSE file in the project root for more information.
using System.Collections;
-using System.Collections.Generic;
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
namespace System.Text.Json
// The current property.
public bool PropertyEnumeratorActive;
+ public int PropertyEnumeratorIndex;
public ExtensionDataWriteStatus ExtensionDataStatus;
- public Dictionary<string, JsonPropertyInfo>.Enumerator PropertyEnumerator;
public JsonPropertyInfo JsonPropertyInfo;
public void Initialize(Type type, JsonSerializerOptions options)
ExtensionDataStatus = ExtensionDataWriteStatus.NotStarted;
IsIDictionaryConstructible = false;
JsonClassInfo = null;
- PropertyEnumerator = default;
+ PropertyEnumeratorIndex = 0;
PropertyEnumeratorActive = false;
PopStackOnEndCollection = false;
PopStackOnEndObject = false;
EndProperty();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EndProperty()
{
IsIDictionaryConstructibleProperty = false;
PopStackOnEndCollection = false;
}
+ // AggressiveInlining used although a large method it is only called from one location and is on a hot path.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void NextProperty()
{
EndProperty();
if (PropertyEnumeratorActive)
{
- if (PropertyEnumerator.MoveNext())
+ int len = JsonClassInfo.PropertyCacheArray.Length;
+ if (PropertyEnumeratorIndex < len)
{
+ if ((PropertyEnumeratorIndex == len - 1) && JsonClassInfo.DataExtensionProperty != null)
+ {
+ ExtensionDataStatus = ExtensionDataWriteStatus.Writing;
+ }
+
+ PropertyEnumeratorIndex++;
PropertyEnumeratorActive = true;
}
else
{
PropertyEnumeratorActive = false;
- ExtensionDataStatus = ExtensionDataWriteStatus.Writing;
}
}
else