@@ -24,7 +24,7 @@ protected void SetSerializerSettingsForJsonApi(JsonSerializerSettings settings)
24
24
}
25
25
26
26
/// <inheritdoc />
27
- public IDisposable OmitDefaultValuesForAttributesInRequestDocument < TRequestDocument , TAttributesObject > ( TRequestDocument requestDocument ,
27
+ public IDisposable WithPartialAttributeSerialization < TRequestDocument , TAttributesObject > ( TRequestDocument requestDocument ,
28
28
params Expression < Func < TAttributesObject , object ? > > [ ] alwaysIncludedAttributeSelectors )
29
29
where TRequestDocument : class
30
30
{
@@ -40,12 +40,12 @@ public IDisposable OmitDefaultValuesForAttributesInRequestDocument<TRequestDocum
40
40
}
41
41
else
42
42
{
43
- throw new ArgumentException ( $ "The expression '{ selector } ' should select a single property. For example: 'article => article.Title'.") ;
43
+ throw new ArgumentException (
44
+ $ "The expression '{ nameof ( alwaysIncludedAttributeSelectors ) } ' should select a single property. For example: 'article => article.Title'.") ;
44
45
}
45
46
}
46
47
47
- _jsonApiJsonConverter . RegisterRequestDocumentForAttributesOmission ( requestDocument ,
48
- new AttributesObjectInfo ( attributeNames , typeof ( TAttributesObject ) ) ) ;
48
+ _jsonApiJsonConverter . RegisterDocument ( requestDocument , new AttributeNamesContainer ( attributeNames , typeof ( TAttributesObject ) ) ) ;
49
49
50
50
return new RequestDocumentRegistrationScope ( _jsonApiJsonConverter , requestDocument ) ;
51
51
}
@@ -69,15 +69,15 @@ private static Expression RemoveConvert(Expression expression)
69
69
70
70
private sealed class JsonApiJsonConverter : JsonConverter
71
71
{
72
- private readonly Dictionary < object , AttributesObjectInfo > _attributesObjectInfoByRequestDocument = new ( ) ;
72
+ private readonly Dictionary < object , AttributeNamesContainer > _alwaysIncludedAttributesByRequestDocument = new ( ) ;
73
73
private readonly Dictionary < Type , ISet < object > > _requestDocumentsByType = new ( ) ;
74
74
private SerializationScope ? _serializationScope ;
75
75
76
76
public override bool CanRead => false ;
77
77
78
- public void RegisterRequestDocumentForAttributesOmission ( object requestDocument , AttributesObjectInfo attributesObjectInfo )
78
+ public void RegisterDocument ( object requestDocument , AttributeNamesContainer alwaysIncludedAttributes )
79
79
{
80
- _attributesObjectInfoByRequestDocument [ requestDocument ] = attributesObjectInfo ;
80
+ _alwaysIncludedAttributesByRequestDocument [ requestDocument ] = alwaysIncludedAttributes ;
81
81
82
82
Type requestDocumentType = requestDocument . GetType ( ) ;
83
83
@@ -91,9 +91,9 @@ public void RegisterRequestDocumentForAttributesOmission(object requestDocument,
91
91
92
92
public void RemoveRegistration ( object requestDocument )
93
93
{
94
- if ( _attributesObjectInfoByRequestDocument . ContainsKey ( requestDocument ) )
94
+ if ( _alwaysIncludedAttributesByRequestDocument . ContainsKey ( requestDocument ) )
95
95
{
96
- _attributesObjectInfoByRequestDocument . Remove ( requestDocument ) ;
96
+ _alwaysIncludedAttributesByRequestDocument . Remove ( requestDocument ) ;
97
97
98
98
Type requestDocumentType = requestDocument . GetType ( ) ;
99
99
_requestDocumentsByType [ requestDocumentType ] . Remove ( requestDocument ) ;
@@ -136,7 +136,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
136
136
}
137
137
else
138
138
{
139
- AttributesObjectInfo ? attributesObjectInfo = _serializationScope . AttributesObjectInScope ;
139
+ AttributeNamesContainer ? attributesObjectInfo = _serializationScope . AttributesObjectInScope ;
140
140
141
141
AssertObjectMatchesSerializationScope ( attributesObjectInfo , value ) ;
142
142
@@ -158,7 +158,7 @@ private void SerializeRequestDocument(JsonWriter writer, object value, JsonSeria
158
158
{
159
159
_serializationScope = new SerializationScope ( ) ;
160
160
161
- if ( _attributesObjectInfoByRequestDocument . TryGetValue ( value , out AttributesObjectInfo ? attributesObjectInfo ) )
161
+ if ( _alwaysIncludedAttributesByRequestDocument . TryGetValue ( value , out AttributeNamesContainer ? attributesObjectInfo ) )
162
162
{
163
163
_serializationScope . AttributesObjectInScope = attributesObjectInfo ;
164
164
}
@@ -173,31 +173,32 @@ private void SerializeRequestDocument(JsonWriter writer, object value, JsonSeria
173
173
}
174
174
}
175
175
176
- private static void AssertObjectMatchesSerializationScope ( [ SysNotNull ] AttributesObjectInfo ? attributesObjectInfo , object value )
176
+ private static void AssertObjectMatchesSerializationScope ( [ SysNotNull ] AttributeNamesContainer ? attributesObjectInfo , object value )
177
177
{
178
178
Type objectType = value . GetType ( ) ;
179
179
180
- if ( attributesObjectInfo == null || ! attributesObjectInfo . MatchesType ( objectType ) )
180
+ if ( attributesObjectInfo == null || ! attributesObjectInfo . MatchesAttributesObjectType ( objectType ) )
181
181
{
182
182
throw new UnreachableCodeException ( ) ;
183
183
}
184
184
}
185
185
186
- private static void SerializeAttributesObject ( AttributesObjectInfo alwaysIncludedAttributes , JsonWriter writer , object value , JsonSerializer serializer )
186
+ private static void SerializeAttributesObject ( AttributeNamesContainer alwaysIncludedAttributes , JsonWriter writer , object value ,
187
+ JsonSerializer serializer )
187
188
{
188
189
AssertRequiredPropertiesAreNotExcluded ( value , alwaysIncludedAttributes , writer ) ;
189
190
190
191
serializer . ContractResolver = new JsonApiAttributeContractResolver ( alwaysIncludedAttributes ) ;
191
192
serializer . Serialize ( writer , value ) ;
192
193
}
193
194
194
- private static void AssertRequiredPropertiesAreNotExcluded ( object value , AttributesObjectInfo alwaysIncludedAttributes , JsonWriter jsonWriter )
195
+ private static void AssertRequiredPropertiesAreNotExcluded ( object value , AttributeNamesContainer alwaysIncludedAttributes , JsonWriter jsonWriter )
195
196
{
196
197
PropertyInfo [ ] propertyInfos = value . GetType ( ) . GetProperties ( ) ;
197
198
198
199
foreach ( PropertyInfo attributesPropertyInfo in propertyInfos )
199
200
{
200
- bool isExplicitlyIncluded = alwaysIncludedAttributes . IsAttributeMarkedForInclusion ( attributesPropertyInfo . Name ) ;
201
+ bool isExplicitlyIncluded = alwaysIncludedAttributes . ContainsAttribute ( attributesPropertyInfo . Name ) ;
201
202
202
203
if ( isExplicitlyIncluded )
203
204
{
@@ -212,7 +213,7 @@ private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInf
212
213
{
213
214
JsonPropertyAttribute jsonPropertyForAttribute = attribute . GetCustomAttributes < JsonPropertyAttribute > ( ) . Single ( ) ;
214
215
215
- if ( jsonPropertyForAttribute . Required != Required . Always )
216
+ if ( jsonPropertyForAttribute . Required is not ( Required . Always or Required . AllowNull ) )
216
217
{
217
218
return ;
218
219
}
@@ -221,8 +222,7 @@ private static void AssertRequiredPropertyIsNotIgnored(object value, PropertyInf
221
222
222
223
if ( isPropertyIgnored )
223
224
{
224
- throw new JsonSerializationException (
225
- $ "Ignored property '{ jsonPropertyForAttribute . PropertyName } ' must have a value because it is required. Path '{ path } '.") ;
225
+ throw new InvalidOperationException ( $ "The following property should not be omitted: { path } .{ jsonPropertyForAttribute . PropertyName } .") ;
226
226
}
227
227
}
228
228
@@ -248,7 +248,7 @@ private static bool DefaultValueEqualsCurrentValue(PropertyInfo propertyInfo, ob
248
248
private sealed class SerializationScope
249
249
{
250
250
private bool _isFirstAttemptToConvertAttributes = true ;
251
- public AttributesObjectInfo ? AttributesObjectInScope { get ; set ; }
251
+ public AttributeNamesContainer ? AttributesObjectInScope { get ; set ; }
252
252
253
253
public bool ShouldConvertAsAttributesObject ( Type type )
254
254
{
@@ -257,7 +257,7 @@ public bool ShouldConvertAsAttributesObject(Type type)
257
257
return false ;
258
258
}
259
259
260
- if ( ! AttributesObjectInScope . MatchesType ( type ) )
260
+ if ( ! AttributesObjectInScope . MatchesAttributesObjectType ( type ) )
261
261
{
262
262
return false ;
263
263
}
@@ -267,26 +267,26 @@ public bool ShouldConvertAsAttributesObject(Type type)
267
267
}
268
268
}
269
269
270
- private sealed class AttributesObjectInfo
270
+ private sealed class AttributeNamesContainer
271
271
{
272
- private readonly ISet < string > _attributesMarkedForInclusion ;
272
+ private readonly ISet < string > _attributeNames ;
273
273
private readonly Type _attributesObjectType ;
274
274
275
- public AttributesObjectInfo ( ISet < string > attributesMarkedForInclusion , Type attributesObjectType )
275
+ public AttributeNamesContainer ( ISet < string > attributeNames , Type attributesObjectType )
276
276
{
277
- ArgumentGuard . NotNull ( attributesMarkedForInclusion ) ;
277
+ ArgumentGuard . NotNull ( attributeNames ) ;
278
278
ArgumentGuard . NotNull ( attributesObjectType ) ;
279
279
280
- _attributesMarkedForInclusion = attributesMarkedForInclusion ;
280
+ _attributeNames = attributeNames ;
281
281
_attributesObjectType = attributesObjectType ;
282
282
}
283
283
284
- public bool IsAttributeMarkedForInclusion ( string name )
284
+ public bool ContainsAttribute ( string name )
285
285
{
286
- return _attributesMarkedForInclusion . Contains ( name ) ;
286
+ return _attributeNames . Contains ( name ) ;
287
287
}
288
288
289
- public bool MatchesType ( Type type )
289
+ public bool MatchesAttributesObjectType ( Type type )
290
290
{
291
291
return _attributesObjectType == type ;
292
292
}
@@ -314,22 +314,24 @@ public void Dispose()
314
314
315
315
private sealed class JsonApiAttributeContractResolver : DefaultContractResolver
316
316
{
317
- private readonly AttributesObjectInfo _attributesObjectInfo ;
317
+ private readonly AttributeNamesContainer _alwaysIncludedAttributes ;
318
318
319
- public JsonApiAttributeContractResolver ( AttributesObjectInfo attributesObjectInfo )
319
+ public JsonApiAttributeContractResolver ( AttributeNamesContainer alwaysIncludedAttributes )
320
320
{
321
- ArgumentGuard . NotNull ( attributesObjectInfo ) ;
321
+ ArgumentGuard . NotNull ( alwaysIncludedAttributes ) ;
322
322
323
- _attributesObjectInfo = attributesObjectInfo ;
323
+ _alwaysIncludedAttributes = alwaysIncludedAttributes ;
324
324
}
325
325
326
326
protected override JsonProperty CreateProperty ( MemberInfo member , MemberSerialization memberSerialization )
327
327
{
328
328
JsonProperty property = base . CreateProperty ( member , memberSerialization ) ;
329
329
330
- if ( _attributesObjectInfo . MatchesType ( property . DeclaringType ! ) )
330
+ bool canOmitAttribute = property . Required != Required . Always ;
331
+
332
+ if ( canOmitAttribute && _alwaysIncludedAttributes . MatchesAttributesObjectType ( property . DeclaringType ! ) )
331
333
{
332
- if ( _attributesObjectInfo . IsAttributeMarkedForInclusion ( property . UnderlyingName ! ) )
334
+ if ( _alwaysIncludedAttributes . ContainsAttribute ( property . UnderlyingName ! ) )
333
335
{
334
336
property . NullValueHandling = NullValueHandling . Include ;
335
337
property . DefaultValueHandling = DefaultValueHandling . Include ;
0 commit comments