Skip to content

Commit 4d7fb2c

Browse files
committed
Refactor internal-only JSON:API extensions support
1 parent 169ffee commit 4d7fb2c

File tree

2 files changed

+34
-22
lines changed

2 files changed

+34
-22
lines changed

src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs

+28-18
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver
4444

4545
var resourceObject = new ResourceObject
4646
{
47-
// The 'attributes' element may occur before 'type', but we need to know the resource type before we can deserialize attributes
48-
// into their corresponding CLR types.
49-
Type = PeekType(ref reader)
47+
// The 'attributes' or 'relationships' element may occur before 'type', but we need to know the resource type
48+
// before we can deserialize attributes/relationships into their corresponding CLR types.
49+
Type = PeekType(reader)
5050
};
5151

5252
ResourceType? resourceType = resourceObject.Type != null ? _resourceGraph.FindResourceType(resourceObject.Type) : null;
@@ -99,7 +99,15 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver
9999
}
100100
case "relationships":
101101
{
102-
resourceObject.Relationships = ReadRelationships(ref reader, options);
102+
if (resourceType != null)
103+
{
104+
resourceObject.Relationships = ReadRelationships(ref reader, options, resourceType);
105+
}
106+
else
107+
{
108+
reader.Skip();
109+
}
110+
103111
break;
104112
}
105113
case "links":
@@ -127,27 +135,27 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver
127135
throw GetEndOfStreamError();
128136
}
129137

130-
private static string? PeekType(ref Utf8JsonReader reader)
138+
private static string? PeekType(Utf8JsonReader reader)
131139
{
132-
// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#an-alternative-way-to-do-polymorphic-deserialization
133-
Utf8JsonReader readerClone = reader;
140+
// This method receives a clone of the reader (which is a struct, and there's no ref modifier on the parameter),
141+
// so advancing here doesn't affect the reader position of the caller.
134142

135-
while (readerClone.Read())
143+
while (reader.Read())
136144
{
137-
if (readerClone.TokenType == JsonTokenType.PropertyName)
145+
if (reader.TokenType == JsonTokenType.PropertyName)
138146
{
139-
string? propertyName = readerClone.GetString();
140-
readerClone.Read();
147+
string? propertyName = reader.GetString();
148+
reader.Read();
141149

142150
switch (propertyName)
143151
{
144152
case "type":
145153
{
146-
return readerClone.GetString();
154+
return reader.GetString();
147155
}
148156
default:
149157
{
150-
readerClone.Skip();
158+
reader.Skip();
151159
break;
152160
}
153161
}
@@ -181,7 +189,7 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver
181189
string extensionNamespace = attributeName[..extensionSeparatorIndex];
182190
string extensionName = attributeName[(extensionSeparatorIndex + 1)..];
183191

184-
ValidateExtensionInAttributes(extensionNamespace, extensionName, reader);
192+
ValidateExtensionInAttributes(extensionNamespace, extensionName, resourceType, reader);
185193
reader.Skip();
186194
continue;
187195
}
@@ -232,12 +240,13 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver
232240
}
233241

234242
// Currently exposed for internal use only, so we don't need a breaking change when adding support for multiple extensions.
235-
private protected virtual void ValidateExtensionInAttributes(string extensionNamespace, string extensionName, Utf8JsonReader reader)
243+
private protected virtual void ValidateExtensionInAttributes(string extensionNamespace, string extensionName, ResourceType resourceType,
244+
Utf8JsonReader reader)
236245
{
237246
throw new JsonException($"Unsupported usage of JSON:API extension '{extensionNamespace}' in attributes.");
238247
}
239248

240-
private Dictionary<string, RelationshipObject?> ReadRelationships(ref Utf8JsonReader reader, JsonSerializerOptions options)
249+
private Dictionary<string, RelationshipObject?> ReadRelationships(ref Utf8JsonReader reader, JsonSerializerOptions options, ResourceType resourceType)
241250
{
242251
var relationships = new Dictionary<string, RelationshipObject?>();
243252

@@ -261,7 +270,7 @@ private protected virtual void ValidateExtensionInAttributes(string extensionNam
261270
string extensionNamespace = relationshipName[..extensionSeparatorIndex];
262271
string extensionName = relationshipName[(extensionSeparatorIndex + 1)..];
263272

264-
ValidateExtensionInRelationships(extensionNamespace, extensionName, reader);
273+
ValidateExtensionInRelationships(extensionNamespace, extensionName, resourceType, reader);
265274
reader.Skip();
266275
continue;
267276
}
@@ -277,7 +286,8 @@ private protected virtual void ValidateExtensionInAttributes(string extensionNam
277286
}
278287

279288
// Currently exposed for internal use only, so we don't need a breaking change when adding support for multiple extensions.
280-
private protected virtual void ValidateExtensionInRelationships(string extensionNamespace, string extensionName, Utf8JsonReader reader)
289+
private protected virtual void ValidateExtensionInRelationships(string extensionNamespace, string extensionName, ResourceType resourceType,
290+
Utf8JsonReader reader)
281291
{
282292
throw new JsonException($"Unsupported usage of JSON:API extension '{extensionNamespace}' in relationships.");
283293
}

test/JsonApiDotNetCoreTests/UnitTests/Serialization/Extensions/ResourceObjectConverterTests.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ public ExtensionAwareResourceObjectConverter(IResourceGraph resourceGraph, JsonA
417417
_requestAccessor = requestAccessor;
418418
}
419419

420-
private protected override void ValidateExtensionInAttributes(string extensionNamespace, string extensionName, Utf8JsonReader reader)
420+
private protected override void ValidateExtensionInAttributes(string extensionNamespace, string extensionName, ResourceType resourceType,
421+
Utf8JsonReader reader)
421422
{
422423
if (extensionNamespace == ExtensionNamespace && IsTypeInfoExtensionEnabled && extensionName == "fail")
423424
{
@@ -429,10 +430,11 @@ private protected override void ValidateExtensionInAttributes(string extensionNa
429430
return;
430431
}
431432

432-
base.ValidateExtensionInAttributes(extensionNamespace, extensionName, reader);
433+
base.ValidateExtensionInAttributes(extensionNamespace, extensionName, resourceType, reader);
433434
}
434435

435-
private protected override void ValidateExtensionInRelationships(string extensionNamespace, string extensionName, Utf8JsonReader reader)
436+
private protected override void ValidateExtensionInRelationships(string extensionNamespace, string extensionName, ResourceType resourceType,
437+
Utf8JsonReader reader)
436438
{
437439
if (extensionNamespace == ExtensionNamespace && IsTypeInfoExtensionEnabled && extensionName == "fail")
438440
{
@@ -444,7 +446,7 @@ private protected override void ValidateExtensionInRelationships(string extensio
444446
return;
445447
}
446448

447-
base.ValidateExtensionInRelationships(extensionNamespace, extensionName, reader);
449+
base.ValidateExtensionInRelationships(extensionNamespace, extensionName, resourceType, reader);
448450
}
449451

450452
private protected override void WriteExtensionInAttributes(Utf8JsonWriter writer, ResourceObject value)

0 commit comments

Comments
 (0)