Skip to content

Commit

Permalink
Create and add instance reference inside ResolveMetadata* (#39330)
Browse files Browse the repository at this point in the history
* Create and add instance reference on ResolveMetadata

* Address suggestions
  • Loading branch information
jozkee authored Jul 20, 2020
1 parent 0655692 commit d8d4d9e
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal sealed override bool OnTryRead(
bool preserveReferences = options.ReferenceHandler != null;
if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
{
if (JsonSerializer.ResolveMetadata(this, ref reader, ref state))
if (JsonSerializer.ResolveMetadataForJsonObject(ref reader, ref state, options))
{
if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
{
Expand All @@ -156,18 +156,6 @@ internal sealed override bool OnTryRead(
if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
{
CreateCollection(ref reader, ref state);

if (state.Current.MetadataId != null)
{
Debug.Assert(CanHaveIdMetadata);

value = (TCollection)state.Current.ReturnValue!;
state.ReferenceResolver.AddReference(state.Current.MetadataId, value);
// Clear metadata name, if the next read fails
// we want to point the JSON path to the property's object.
state.Current.JsonPropertyName = null;
}

state.Current.ObjectState = StackFrameObjectState.CreatedObject;
}

Expand Down Expand Up @@ -308,5 +296,8 @@ internal sealed override bool OnTryWrite(

return success;
}

internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
=> CreateCollection(ref reader, ref state);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ internal override bool OnTryRead(
// Handle the metadata properties.
if (preserveReferences && state.Current.ObjectState < StackFrameObjectState.PropertyValue)
{
if (JsonSerializer.ResolveMetadata(this, ref reader, ref state))
if (JsonSerializer.ResolveMetadataForJsonArray(ref reader, ref state, options))
{
if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
{
Expand All @@ -131,23 +131,6 @@ internal override bool OnTryRead(
if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
{
CreateCollection(ref reader, ref state, options);

if (state.Current.MetadataId != null)
{
value = (TCollection)state.Current.ReturnValue!;

// TODO: https://github.com/dotnet/runtime/issues/37168
//Separate logic for IEnumerable to call AddReference when the reader is at `$id`, in order to avoid remembering the last metadata.

// Remember the prior metadata and temporarily use '$id' to write it in the path in case AddReference throws
// in this case, the last property seen will be '$values' when we reach this point.
byte[]? lastMetadataProperty = state.Current.JsonPropertyName;
state.Current.JsonPropertyName = JsonSerializer.s_idPropertyName;

state.ReferenceResolver.AddReference(state.Current.MetadataId, value);
state.Current.JsonPropertyName = lastMetadataProperty;
}

state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.ElementClassInfo!.PropertyInfoForClassInfo;
state.Current.ObjectState = StackFrameObjectState.CreatedObject;
}
Expand Down Expand Up @@ -203,34 +186,25 @@ internal override bool OnTryRead(
{
state.Current.ObjectState = StackFrameObjectState.EndToken;

// Read the EndObject for $values.
if (state.Current.MetadataId != null)
// Read the EndObject token for an array with preserve semantics.
if (state.Current.ValidateEndTokenOnArray)
{
if (!reader.Read())
{
value = default;
return false;
}

if (reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType == JsonTokenType.PropertyName)
{
state.Current.JsonPropertyName = reader.GetSpan().ToArray();
}

ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(typeToConvert, reader);
}
}
}

if (state.Current.ObjectState < StackFrameObjectState.EndTokenValidation)
{
if (state.Current.MetadataId != null)
if (state.Current.ValidateEndTokenOnArray)
{
if (reader.TokenType != JsonTokenType.EndObject)
{
ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(typeToConvert, reader);
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
ThrowHelper.ThrowJsonException_MetadataPreservedArrayInvalidProperty(ref state, typeToConvert, reader);
}
}
}
Expand Down Expand Up @@ -299,5 +273,8 @@ internal sealed override bool OnTryWrite(
}

protected abstract bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state);

internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
=> CreateCollection(ref reader, ref state, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ protected override void Add(in TElement value, ref ReadStack state)
((List<TElement>)state.Current.ReturnValue!).Add(value);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (!TypeToConvert.IsAssignableFrom(RuntimeType))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
{
if (options.ReferenceHandler != null)
{
if (JsonSerializer.ResolveMetadata(this, ref reader, ref state))
if (JsonSerializer.ResolveMetadataForJsonObject(ref reader, ref state, options))
{
if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject)
{
Expand All @@ -91,8 +91,6 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
return false;
}
}

state.Current.ObjectState = StackFrameObjectState.PropertyValue;
}

if (state.Current.ObjectState < StackFrameObjectState.CreatedObject)
Expand All @@ -103,13 +101,6 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
}

obj = state.Current.JsonClassInfo.CreateObject!()!;
if (state.Current.MetadataId != null)
{
state.ReferenceResolver.AddReference(state.Current.MetadataId, obj);
// Clear metadata name, if the next read fails
// we want to point the JSON path to the property's object.
state.Current.JsonPropertyName = null;
}

state.Current.ReturnValue = obj;
state.Current.ObjectState = StackFrameObjectState.CreatedObject;
Expand Down Expand Up @@ -416,5 +407,16 @@ protected bool ReadAheadPropertyValue(ref ReadStack state, ref Utf8JsonReader re

return true;
}

internal sealed override void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
if (state.Current.JsonClassInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(state.Current.JsonClassInfo.Type, ref reader, ref state);
}

object obj = state.Current.JsonClassInfo.CreateObject!()!;
state.Current.ReturnValue = obj;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,10 @@ internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
internal ConstructorInfo? ConstructorInfo { get; set; }

internal virtual void Initialize(JsonSerializerOptions options) { }

/// <summary>
/// Creates the instance and assigns it to state.Current.ReturnValue.
/// </summary>
internal virtual void CreateInstanceForReferenceResolver(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { }
}
}
Loading

0 comments on commit d8d4d9e

Please sign in to comment.