Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -447,4 +447,45 @@
<data name="NotNodeJsonElementParent" xml:space="preserve">
<value>This JsonElement instance was not built from a JsonNode and is immutable.</value>
</data>
</root>
<data name="MetadataCannotParsePreservedObjectToImmutable" xml:space="preserve">
<value>Cannot parse preserved object to Array or Immutable. Type {0}.</value>
</data>
<data name="MetadataDuplicateIdFound" xml:space="preserve">
<value>Duplicated identifier '{0}' found while reading preserved object.</value>
</data>
<data name="MetadataIdIsNotFirstProperty" xml:space="preserve">
<value>The metadata property $id must be the first property in the JSON object.</value>
</data>
<data name="MetadataInvalidReferenceToValueType" xml:space="preserve">
<value>Invalid reference to value type {0}.</value>
</data>
<data name="MetadataInvalidTokenAfterValues" xml:space="preserve">
<value>Invalid token after $values metadata property.</value>
</data>
<data name="MetadataMissingIdBeforeValues" xml:space="preserve">
<value>Missing $id before $values on preserved array.</value>
</data>
<data name="MetadataPreservedArrayFailed" xml:space="preserve">
<value>Deserialization failed for one of these reasons:
1. {0}
2. {1}</value>
</data>
<data name="MetadataPreservedArrayInvalidProperty" xml:space="preserve">
<value>Invalid property in preserved array.</value>
</data>
<data name="MetadataPreservedArrayValuesNotFound" xml:space="preserve">
<value>Metadata $values property was not found in preserved array.</value>
</data>
<data name="MetadataReferenceCannotContainOtherProperties" xml:space="preserve">
<value>Reference objects must not contain other properties except for $ref.</value>
</data>
<data name="MetadataReferenceNotFound" xml:space="preserve">
<value>Reference not found.</value>
</data>
<data name="MetadataValueWasNotString" xml:space="preserve">
<value>Value for metadata properties $id and $ref must be of type string.</value>
</data>
<data name="MetadataInvalidPropertyWithLeadingSign" xml:space="preserve">
<value>Properties that start with '$' are not allowed on preserve mode, either escape the character or turn off preserve references.</value>
</data>
</root>
2 changes: 0 additions & 2 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
<Compile Include="System\Text\Json\Serialization\DefaultReferenceResolver.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceEqualsEqualityComparer.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceHandling.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceHandlingStrategy.cs" />
<Compile Include="System\Text\Json\Serialization\ResolvedReferenceHandling.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleMetadata.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleReferences.cs" />
<Compile Include="System\Text\Json\ThrowHelper.cs" />
<Compile Include="System\Text\Json\ThrowHelper.Serialization.cs" />
<Compile Include="System\Text\Json\Document\JsonDocument.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ public DefaultReferenceResolver()
// On deserialization: key is TKey.
// On serialization: value is TKey.
_referenceMapper = new Dictionary<object, object>();
//TODO: add second dictionary that uses reference equals.
}

// Used on deserialization.
public void AddReference(string key, object value)
{
if (!_referenceMapper.TryAdd(key, value))
if (_referenceMapper.ContainsKey(key))
{
throw new JsonException($"Duplicated $id \"{key}\" found while preserving reference.");
ThrowHelper.ThrowJsonException_MetadataDuplicateIdFound(key);
}

_referenceMapper[key] = value;
}

// Used on serialization.
Expand Down Expand Up @@ -52,7 +55,7 @@ public object ResolveReference(string key)
{
if (!_referenceMapper.TryGetValue(key, out object value))
{
throw new JsonException("Reference not found.");
ThrowHelper.ThrowJsonException_MetadataReferenceNotFound();
}

return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,78 +53,7 @@ private static void HandleStartDictionary(JsonSerializerOptions options, ref Rea

state.Current.ReturnValue = classInfo.CreateObject();
}
else
{
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(classInfo.Type);
}

return;
}

state.Current.CollectionPropertyInitialized = true;

object value = ReadStackFrame.CreateDictionaryValue(ref state);
if (value != null)
{
state.Current.DetermineIfDictionaryCanBePopulated(value);

if (state.Current.ReturnValue != null)
{
state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value);
}
else
{
// A dictionary is being returned directly, or a nested dictionary.
state.Current.ReturnValue = value;
}
}
}

private static void HandleStartDictionaryRef(JsonSerializerOptions options, ref ReadStack state)
{
Debug.Assert(!state.Current.IsProcessingEnumerable());

JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
if (jsonPropertyInfo == null)
{
jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootProperty(options);
}

Debug.Assert(jsonPropertyInfo != null);

// A nested object or dictionary, so push new frame.
if (state.Current.CollectionPropertyInitialized)
{
state.Push();
state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo;
state.Current.InitializeJsonPropertyInfo();

JsonClassInfo classInfo = state.Current.JsonClassInfo;

if (state.Current.IsProcessingDictionary())
{
object dictValue = ReadStackFrame.CreateDictionaryValue(ref state);

// If value is not null, then we don't have a converter so apply the value.
if (dictValue != null)
{
state.Current.ReturnValue = dictValue;
state.Current.DetermineIfDictionaryCanBePopulated(state.Current.ReturnValue);
}

state.Current.CollectionPropertyInitialized = true;
}
else if (state.Current.IsProcessingObject(ClassType.Object))
{
if (classInfo.CreateObject == null)
{
ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(classInfo.Type);
}

state.Current.ReturnValue = classInfo.CreateObject();
}
// If is reference preserved array.
else if (state.Current.IsProcessingEnumerable())
else if (options.ReferenceHandling.ShouldReadPreservedReferences() && state.Current.IsProcessingEnumerable())
{
Type preservedObjType = state.Current.JsonClassInfo.PolicyProperty.GetJsonPreservedReferenceType();
// Re-Initialize the current frame.
Expand Down Expand Up @@ -213,60 +142,5 @@ private static void HandleEndDictionary(JsonSerializerOptions options, ref ReadS
}
}
}

private static void HandleEndDictionaryRef(JsonSerializerOptions options, ref ReadStack state)
{
Debug.Assert(!state.Current.SkipProperty);

if (state.Current.IsProcessingProperty(ClassType.Dictionary))
{
if (state.Current.TempDictionaryValues != null)
{
JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options));
state.Current.EndProperty();
}
else
{
// 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 state);
}
else
{
// We added the items to the dictionary already.
state.Current.EndProperty();
}
}
}
else
{
object value;
if (state.Current.TempDictionaryValues != null)
{
JsonDictionaryConverter converter = state.Current.JsonPropertyInfo.DictionaryConverter;
value = converter.CreateFromDictionary(ref state, state.Current.TempDictionaryValues, options);
}
else
{
value = state.Current.ReturnValue;
}

if (state.IsLastFrame)
{
// Set the return value directly since this will be returned to the user.
state.Current.Reset();
state.Current.ReturnValue = value;
}
else
{
state.Pop();
ApplyObjectToEnumerable(value, ref state);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ private static void HandleMetadataPropertyValue(ref Utf8JsonReader reader, ref R
{
if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException("Value for metadata properties cannot be other than string.");
ThrowHelper.ThrowJsonException_MetadataValueWasNotString();
}

MetadataPropertyName metadata = state.Current.MetadataProperty;
Expand Down Expand Up @@ -73,24 +73,24 @@ internal static MetadataPropertyName GetMetadataPropertyName(ReadOnlySpan<byte>

// Fail state.
// Set PropertyInfo or KeyName to write down the conflicting property name in JsonException.Path
if (!state.Current.IsProcessingDictionary())
if (state.Current.IsProcessingDictionary())
{
JsonPropertyInfo info = JsonPropertyInfo.s_metadataProperty;
info.JsonPropertyName = propertyName.ToArray();
state.Current.JsonPropertyInfo = info;
state.Current.KeyName = reader.GetString();
}
else
{
state.Current.KeyName = reader.GetString();
JsonPropertyInfo info = JsonPropertyInfo.s_metadataProperty;
info.JsonPropertyName = propertyName.ToArray();
state.Current.JsonPropertyInfo = info;
}

throw new JsonException("Properties that start with '$' are not allowed on preserve mode, you must either escape '$' or turn off preserve references.");
ThrowHelper.ThrowJsonException_MetadataInvalidPropertyWithLeadingSign();
}

return MetadataPropertyName.NoMetadata;
}

private static void HandleReference(JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
private static void HandleReference(ref ReadStack state)
{
object referenceValue = state.ResolveReference(state.Current.ReferenceId);
if (state.Current.IsProcessingProperty(ClassType.Dictionary))
Expand All @@ -101,30 +101,31 @@ private static void HandleReference(JsonSerializerOptions options, ref ReadStack
else
{
state.Current.ReturnValue = referenceValue;
HandleEndObjectRef(ref state);
HandleEndObject(ref state);
}

state.Current.ShouldHandleReference = false;
}

internal static void SetAsPreserved(ref ReadStackFrame frame)
{
bool alreadyPreserving;
//bool alreadyPreserving;
if (frame.IsProcessingProperty(ClassType.Dictionary))
{
alreadyPreserving = frame.DictionaryPropertyIsPreserved;
//alreadyPreserving = frame.DictionaryPropertyIsPreserved;
frame.DictionaryPropertyIsPreserved = true;
}
else
{
alreadyPreserving = frame.IsPreserved;
//alreadyPreserving = frame.IsPreserved;
frame.IsPreserved = true;
}

if (alreadyPreserving)
{
throw new JsonException("Object already defines a reference identifier.");
}
// Unreachable, if more than one $id, error "$id must be the first property" will pop-up.
//if (alreadyPreserving)
//{
// throw new JsonException("Object already defines a reference identifier.");
//}
}
}

Expand Down
Loading