Skip to content

Commit 0abb063

Browse files
committed
feat: reorganisation inheritance serialization layer
1 parent 4f592bd commit 0abb063

29 files changed

+474
-324
lines changed

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ public static void AddJsonApiInternals(
227227
services.AddScoped<IJsonApiDeserializer, RequestDeserializer>();
228228
services.AddScoped<IRequestSerializer, RequestSerializer>();
229229
services.AddScoped<IResponseDeserializer, ResponseDeserializer>();
230-
services.AddScoped<ISerializerSettingsProvider, ResponseSerializerSettingsProvider>();
230+
services.AddScoped<IResourceObjectBuilderSettingsProvider, ResponseSerializerSettingsProvider>();
231231
services.AddScoped<IJsonApiSerializerFactory, ResponseSerializerFactory>();
232232

233233
if (jsonApiOptions.EnableResourceHooks)

src/JsonApiDotNetCore/Models/JsonApiDocuments/Document.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ public class Document : ExposableData<ResourceObject>
2121
[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
2222
public TopLevelLinks Links { get; set; }
2323

24-
///// <summary>
25-
///// see "responses" in https://jsonapi.org/format/#fetching-relationships
26-
///// </summary>
27-
//[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
28-
//public RelationshipLinks TopLevelRelationshipLinks { get; set; }
29-
3024
/// <summary>
3125
/// see "included" in https://jsonapi.org/format/#document-top-level
3226
/// </summary>

src/JsonApiDotNetCore/Models/JsonApiDocuments/ExposableData.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ public class ExposableData<T> where T : class
1818
/// </summary>
1919
/// <remarks>
2020
/// Moving this method to the derived class where it is needed only in the
21-
/// case of <see cref="RelationshipData"/> would make more sense, but
21+
/// case of <see cref="RelationshipEntry"/> would make more sense, but
2222
/// Newtonsoft does not support this.
2323
/// </remarks>
2424
public bool ShouldSerializeData()
2525
{
26-
if (GetType() == typeof(RelationshipData))
26+
if (GetType() == typeof(RelationshipEntry))
2727
return IsPopulated;
2828
return true;
2929
}
@@ -51,7 +51,7 @@ public bool ShouldSerializeData()
5151
/// </summary>
5252
internal bool IsPopulated { get; private set; } = false;
5353

54-
internal bool HasData { get { return IsPopulated && ((IsManyData && ManyData.Any()) || SingleData != null); } }
54+
internal bool HasResource { get { return IsPopulated && ((IsManyData && ManyData.Any()) || SingleData != null); } }
5555

5656
/// <summary>
5757
/// Gets the "single" or "many" data depending on which one was

src/JsonApiDotNetCore/Models/JsonApiDocuments/RelationshipData.cs renamed to src/JsonApiDotNetCore/Models/JsonApiDocuments/RelationshipEntry.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using System.Collections.Generic;
2-
using System.Linq;
31
using JsonApiDotNetCore.Models.Links;
42
using Newtonsoft.Json;
5-
using Newtonsoft.Json.Linq;
63

74
namespace JsonApiDotNetCore.Models
85
{
9-
public class RelationshipData : ExposableData<ResourceIdentifierObject>
6+
public class RelationshipEntry : ExposableData<ResourceIdentifierObject>
107
{
118
[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
129
public RelationshipLinks Links { get; set; }

src/JsonApiDotNetCore/Models/JsonApiDocuments/ResourceObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class ResourceObject : ResourceIdentifierObject
1010
public Dictionary<string, object> Attributes { get; set; }
1111

1212
[JsonProperty("relationships", NullValueHandling = NullValueHandling.Ignore)]
13-
public Dictionary<string, RelationshipData> Relationships { get; set; }
13+
public Dictionary<string, RelationshipEntry> Relationships { get; set; }
1414

1515
[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
1616
public ResourceLinks Links { get; set; }

src/JsonApiDotNetCore/Query/Contracts/IAttributeBehaviourService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Query
44
{
55
/// <summary>
66
/// Encapsulates client overrides of omit null and omit default values behaviour
7-
/// in <see cref="SerializerSettings"/>
7+
/// in <see cref="ResourceObjectBuilderSettings"/>
88
/// </summary>
99
public interface IAttributeBehaviourService: IQueryParameterService
1010
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using JsonApiDotNetCore.Internal.Contracts;
3+
using JsonApiDotNetCore.Models;
4+
5+
namespace JsonApiDotNetCore.Serialization
6+
{
7+
public class RequestResourceObjectBuilder : BaseResourceObjectBuilder, IResourceObjectBuilder
8+
{
9+
public RequestResourceObjectBuilder(IResourceGraph resourceGraph, IContextEntityProvider provider, ResourceObjectBuilderSettings settings) : base(resourceGraph, provider, settings)
10+
{
11+
}
12+
13+
protected override RelationshipEntry GetRelationshipData(RelationshipAttribute relationship, IIdentifiable entity)
14+
{
15+
return new RelationshipEntry { Data = GetRelatedResourceLinkage(relationship, entity) };
16+
}
17+
}
18+
}

src/JsonApiDotNetCore/Serialization/Client/RequestSerializer.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Linq.Expressions;
55
using JsonApiDotNetCore.Internal.Contracts;
66
using JsonApiDotNetCore.Models;
7-
using JsonApiDotNetCore.Serialization.Server;
87
using JsonApiDotNetCore.Services;
98
using Newtonsoft.Json;
109

@@ -23,9 +22,8 @@ public class RequestSerializer : BaseDocumentBuilder, IRequestSerializer
2322
private readonly IFieldsExplorer _fieldExplorer;
2423
public RequestSerializer(IFieldsExplorer fieldExplorer,
2524
IContextEntityProvider provider,
26-
IResourceGraph resourceGraph,
27-
ISerializerSettingsProvider settingsProvider)
28-
: base(resourceGraph, provider, settingsProvider.Get())
25+
IResourceObjectBuilder resourceObjectBuilder)
26+
: base(resourceObjectBuilder, provider)
2927
{
3028
_fieldExplorer = fieldExplorer;
3129
_attributesToSerializeCache = new Dictionary<Type, List<AttrAttribute>>();

src/JsonApiDotNetCore/Serialization/Client/ResponseDeserializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public DeserializedListResponse<TResource> DeserializeList<TResource>(string bod
5151
/// <param name="entity">The entity that was constructed from the document's body</param>
5252
/// <param name="field">The metadata for the exposed field</param>
5353
/// <param name="data">Relationship data for <paramref name="entity"/>. Is null when <paramref name="field"/> is not a <see cref="RelationshipAttribute"/></param>
54-
protected override void AfterProcessField(IIdentifiable entity, IResourceField field, RelationshipData data = null)
54+
protected override void AfterProcessField(IIdentifiable entity, IResourceField field, RelationshipEntry data = null)
5555
{
5656
// Client deserializers do not need additional processing for attributes.
5757
if (field is AttrAttribute)

src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ protected BaseDocumentParser(IContextEntityProvider provider)
3838
/// <param name="entity">The entity that was constructed from the document's body</param>
3939
/// <param name="field">The metadata for the exposed field</param>
4040
/// <param name="data">Relationship data for <paramref name="entity"/>. Is null when <paramref name="field"/> is not a <see cref="RelationshipAttribute"/></param>
41-
protected abstract void AfterProcessField(IIdentifiable entity, IResourceField field, RelationshipData data = null);
41+
protected abstract void AfterProcessField(IIdentifiable entity, IResourceField field, RelationshipEntry data = null);
4242

4343
/// <inheritdoc/>
4444
protected object Deserialize(string body)
@@ -54,7 +54,7 @@ protected object Deserialize(string body)
5454
}
5555

5656
if (_document.SingleData == null) return null;
57-
return ParseResourceObject(_document.SingleData);
57+
return ParseResourceObject(_document.SingleData);
5858
}
5959

6060
/// <summary>
@@ -88,15 +88,15 @@ protected IIdentifiable SetAttributes(IIdentifiable entity, Dictionary<string, o
8888
/// <param name="relationshipsValues">Relationships and their values, as in the serialized content</param>
8989
/// <param name="relationshipAttributes">Exposed relatinships for <paramref name="entity"/></param>
9090
/// <returns></returns>
91-
protected IIdentifiable SetRelationships(IIdentifiable entity, Dictionary<string, RelationshipData> relationshipsValues, List<RelationshipAttribute> relationshipAttributes)
91+
protected IIdentifiable SetRelationships(IIdentifiable entity, Dictionary<string, RelationshipEntry> relationshipsValues, List<RelationshipAttribute> relationshipAttributes)
9292
{
9393
if (relationshipsValues == null || relationshipsValues.Count == 0)
9494
return entity;
9595

9696
var entityProperties = entity.GetType().GetProperties();
9797
foreach (var attr in relationshipAttributes)
9898
{
99-
if (!relationshipsValues.TryGetValue(attr.PublicRelationshipName, out RelationshipData relationshipData) || !relationshipData.IsPopulated)
99+
if (!relationshipsValues.TryGetValue(attr.PublicRelationshipName, out RelationshipEntry relationshipData) || !relationshipData.IsPopulated)
100100
continue;
101101

102102
if (attr is HasOneAttribute hasOne)
@@ -159,7 +159,7 @@ private IIdentifiable ParseResourceObject(ResourceObject data)
159159
private object SetHasOneRelationship(IIdentifiable entity,
160160
PropertyInfo[] entityProperties,
161161
HasOneAttribute attr,
162-
RelationshipData relationshipData)
162+
RelationshipEntry relationshipData)
163163
{
164164
var rio = (ResourceIdentifierObject)relationshipData.Data;
165165
var relatedId = rio?.Id ?? null;
@@ -173,7 +173,7 @@ private object SetHasOneRelationship(IIdentifiable entity,
173173
SetForeignKey(entity, foreignKeyProperty, attr, relatedId);
174174

175175
SetNavigation(entity, attr, relatedId);
176-
176+
177177
/// depending on if this base parser is used client-side or server-side,
178178
/// different additional processing per field needs to be executed.
179179
AfterProcessField(entity, attr, relationshipData);
@@ -222,7 +222,7 @@ private void SetNavigation(IIdentifiable entity, HasOneAttribute attr, string re
222222
/// </summary>
223223
private object SetHasManyRelationship(IIdentifiable entity,
224224
HasManyAttribute attr,
225-
RelationshipData relationshipData)
225+
RelationshipEntry relationshipData)
226226
{
227227
if (relationshipData.Data != null)
228228
{ // if the relationship is set to null, no need to set the navigation property to null: this is the default value.

src/JsonApiDotNetCore/Serialization/Common/DocumentBuilder.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ namespace JsonApiDotNetCore.Serialization
99
/// Abstract base class for serialization that extends <see cref="ResourceObjectBuilder"/>.
1010
/// Converts entities in to <see cref="ResourceObject"/>s and wraps them in a <see cref="Document"/>.
1111
/// </summary>
12-
public abstract class BaseDocumentBuilder : ResourceObjectBuilder
12+
public abstract class BaseDocumentBuilder
1313
{
14-
protected BaseDocumentBuilder(IResourceGraph resourceGraph, IContextEntityProvider provider, SerializerSettings behaviour) : base(resourceGraph, provider, behaviour) { }
14+
protected readonly IContextEntityProvider _provider;
15+
protected readonly IResourceObjectBuilder _resourceObjectBuilder;
16+
protected BaseDocumentBuilder(IResourceObjectBuilder resourceObjectBuilder, IContextEntityProvider provider)
17+
{
18+
_resourceObjectBuilder = resourceObjectBuilder;
19+
_provider = provider;
20+
}
1521

1622
/// <summary>
1723
/// Builds a <see cref="Document"/> for <paramref name="entity"/>.
@@ -26,7 +32,7 @@ protected Document Build(IIdentifiable entity, List<AttrAttribute> attributes, L
2632
if (entity == null)
2733
return new Document();
2834

29-
return new Document { Data = BuildResourceObject(entity, attributes, relationships) };
35+
return new Document { Data = _resourceObjectBuilder.Build(entity, attributes, relationships) };
3036
}
3137

3238
/// <summary>
@@ -41,7 +47,7 @@ protected Document Build(IEnumerable entities, List<AttrAttribute> attributes, L
4147
{
4248
var data = new List<ResourceObject>();
4349
foreach (IIdentifiable entity in entities)
44-
data.Add(BuildResourceObject(entity, attributes, relationships));
50+
data.Add(_resourceObjectBuilder.Build(entity, attributes, relationships));
4551

4652
return new Document { Data = data };
4753
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Models;
3+
4+
namespace JsonApiDotNetCore.Serialization
5+
{
6+
/// <summary>
7+
/// Abstract base class for serialization. Converts entities in to <see cref="ResourceObject"/>s
8+
/// given a list of attributes and relationships.
9+
/// </summary>
10+
public interface IResourceObjectBuilder
11+
{
12+
/// <summary>
13+
/// Converts <paramref name="entity"/> into a <see cref="ResourceObject"/>.
14+
/// Adds the attributes and relationships that are enlisted in <paramref name="attrs"/> and <paramref name="rels"/>
15+
/// </summary>
16+
/// <param name="entity">Entity to build a Resource Object for</param>
17+
/// <param name="attributes">Attributes to include in the building process</param>
18+
/// <param name="relationships">Relationships to include in the building process</param>
19+
/// <returns>The resource object that was built</returns>
20+
ResourceObject Build(IIdentifiable entity, IEnumerable<AttrAttribute> attributes, IEnumerable<RelationshipAttribute> relationships);
21+
}
22+
}

src/JsonApiDotNetCore/Serialization/Common/ResourceObjectBuilder.cs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@
88

99
namespace JsonApiDotNetCore.Serialization
1010
{
11+
1112
/// <summary>
1213
/// Abstract base class for serialization. Converts entities in to <see cref="ResourceObject"/>s
1314
/// given a list of attributes and relationships.
1415
/// </summary>
15-
public abstract class ResourceObjectBuilder
16+
public abstract class BaseResourceObjectBuilder
1617
{
1718
protected readonly IResourceGraph _resourceGraph;
1819
protected readonly IContextEntityProvider _provider;
19-
private readonly SerializerSettings _settings;
20+
private readonly ResourceObjectBuilderSettings _settings;
2021
private const string _identifiablePropertyName = nameof(Identifiable.Id);
2122

22-
protected ResourceObjectBuilder(IResourceGraph resourceGraph, IContextEntityProvider provider, SerializerSettings settings)
23+
protected BaseResourceObjectBuilder(IResourceGraph resourceGraph, IContextEntityProvider provider, ResourceObjectBuilderSettings settings)
2324
{
2425
_resourceGraph = resourceGraph;
2526
_provider = provider;
@@ -28,13 +29,13 @@ protected ResourceObjectBuilder(IResourceGraph resourceGraph, IContextEntityProv
2829

2930
/// <summary>
3031
/// Converts <paramref name="entity"/> into a <see cref="ResourceObject"/>.
31-
/// Adds the attributes and relationships that are enlisted in <paramref name="attrs"/> and <paramref name="rels"/>
32+
/// Adds the attributes and relationships that are enlisted in <paramref name="attributes"/> and <paramref name="relationships"/>
3233
/// </summary>
3334
/// <param name="entity">Entity to build a Resource Object for</param>
3435
/// <param name="attributes">Attributes to include in the building process</param>
3536
/// <param name="relationships">Relationships to include in the building process</param>
3637
/// <returns>The resource object that was built</returns>
37-
protected ResourceObject BuildResourceObject(IIdentifiable entity, IEnumerable<AttrAttribute> attributes, IEnumerable<RelationshipAttribute> relationships)
38+
public ResourceObject Build(IIdentifiable entity, IEnumerable<AttrAttribute> attributes, IEnumerable<RelationshipAttribute> relationships)
3839
{
3940
var resourceContext = _provider.GetContextEntity(entity.GetType());
4041

@@ -55,40 +56,40 @@ protected ResourceObject BuildResourceObject(IIdentifiable entity, IEnumerable<A
5556
{
5657
var relData = GetRelationshipData(rel, entity);
5758
if (relData != null)
58-
(ro.Relationships = ro.Relationships ?? new Dictionary<string, RelationshipData>()).Add(rel.PublicRelationshipName, relData);
59+
(ro.Relationships = ro.Relationships ?? new Dictionary<string, RelationshipEntry>()).Add(rel.PublicRelationshipName, relData);
5960
}
60-
6161

6262
return ro;
6363
}
6464

65-
6665
private void AddAttribute(IIdentifiable entity, ResourceObject ro, AttrAttribute attr)
6766
{
6867
var value = attr.GetValue(entity);
69-
if ( !(value == default && _settings.OmitDefaultValuedAttributes) && !(value == null && _settings.OmitDefaultValuedAttributes))
68+
if (!(value == default && _settings.OmitDefaultValuedAttributes) && !(value == null && _settings.OmitDefaultValuedAttributes))
7069
ro.Attributes.Add(attr.PublicAttributeName, value);
7170
}
7271

7372
/// <summary>
74-
/// Builds the <see cref="RelationshipData"/> entries of the "relationships
73+
/// Builds the <see cref="RelationshipEntry"/> entries of the "relationships
7574
/// objects" The default behaviour is to just construct a resource linkage
7675
/// with the "data" field populated with "single" or "many" data.
7776
/// Depending on the requirements of the implementation (server or client serializer),
7877
/// this may be overridden.
7978
/// </summary>
80-
protected virtual RelationshipData GetRelationshipData(RelationshipAttribute relationship, IIdentifiable entity)
79+
protected abstract RelationshipEntry GetRelationshipData(RelationshipAttribute relationship, IIdentifiable entity);
80+
81+
protected object GetRelatedResourceLinkage(RelationshipAttribute relationship, IIdentifiable entity)
8182
{
8283
if (relationship is HasOneAttribute hasOne)
83-
return new RelationshipData { Data = GetRelatedResourceIdentifier(hasOne, entity) };
84+
return GetRelatedResourceLinkage(hasOne, entity);
8485

85-
return new RelationshipData { Data = GetRelatedResourceLinkage((HasManyAttribute)relationship, entity) };
86+
return GetRelatedResourceLinkage((HasManyAttribute)relationship, entity);
8687
}
8788

8889
/// <summary>
8990
/// Builds a <see cref="ResourceIdentifierObject"/> for a HasOne relationship
9091
/// </summary>
91-
protected ResourceIdentifierObject GetRelatedResourceIdentifier(HasOneAttribute attr, IIdentifiable entity)
92+
private ResourceIdentifierObject GetRelatedResourceLinkage(HasOneAttribute attr, IIdentifiable entity)
9293
{
9394
var relatedEntity = (IIdentifiable)_resourceGraph.GetRelationshipValue(entity, attr);
9495
if (relatedEntity == null && IsRequiredToOneRelationship(attr, entity))
@@ -103,7 +104,7 @@ protected ResourceIdentifierObject GetRelatedResourceIdentifier(HasOneAttribute
103104
/// <summary>
104105
/// Builds the <see cref="ResourceIdentifierObject"/>s for a HasMany relationship
105106
/// </summary>
106-
protected List<ResourceIdentifierObject> GetRelatedResourceLinkage(HasManyAttribute attr, IIdentifiable entity)
107+
private List<ResourceIdentifierObject> GetRelatedResourceLinkage(HasManyAttribute attr, IIdentifiable entity)
107108
{
108109
var relatedEntities = (IEnumerable)_resourceGraph.GetRelationshipValue(entity, attr);
109110
var manyData = new List<ResourceIdentifierObject>();

src/JsonApiDotNetCore/Serialization/Common/SerializerSettings.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ namespace JsonApiDotNetCore.Serialization
66
/// Options used to configure how fields of a model get serialized into
77
/// a json:api <see cref="Document"/>.
88
/// </summary>
9-
public class SerializerSettings
9+
public class ResourceObjectBuilderSettings
1010
{
1111
/// <param name="omitNullValuedAttributes">Omit null values from attributes</param>
12-
public SerializerSettings(bool omitNullValuedAttributes = false, bool omitDefaultValuedAttributes = false)
12+
public ResourceObjectBuilderSettings(bool omitNullValuedAttributes = false, bool omitDefaultValuedAttributes = false)
1313
{
1414
OmitNullValuedAttributes = omitNullValuedAttributes;
1515
OmitDefaultValuedAttributes = omitDefaultValuedAttributes;

0 commit comments

Comments
 (0)