Skip to content

Commit 330cb7e

Browse files
committed
fix(deserializer): skip foreign key if data not in relationship
also, improve the error message
1 parent a31f52a commit 330cb7e

File tree

2 files changed

+112
-6
lines changed

2 files changed

+112
-6
lines changed

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,6 @@ private object SetHasOneRelationship(object entity,
164164
ContextEntity contextEntity,
165165
Dictionary<string, RelationshipData> relationships)
166166
{
167-
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.InternalRelationshipName}Id");
168-
169-
if (entityProperty == null)
170-
throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.InternalRelationshipName}");
171-
172167
var relationshipName = attr.PublicRelationshipName;
173168

174169
if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData))
@@ -181,6 +176,12 @@ private object SetHasOneRelationship(object entity,
181176
if (data == null) return entity;
182177

183178
var newValue = data["id"];
179+
180+
var foreignKey = attr.InternalRelationshipName + "Id";
181+
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey);
182+
if (entityProperty == null)
183+
throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain a foreign key property '{foreignKey}' for has one relationship '{attr.InternalRelationshipName}'");
184+
184185
var convertedValue = TypeHelper.ConvertType(newValue, entityProperty.PropertyType);
185186

186187
_jsonApiContext.RelationshipsToUpdate[relationshipAttr] = convertedValue;

test/UnitTests/Serialization/JsonApiDeSerializerTests.cs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using JsonApiDotNetCore.Builders;
34
using JsonApiDotNetCore.Configuration;
45
using JsonApiDotNetCore.Internal.Generics;
@@ -196,6 +197,98 @@ public void Immutable_Attrs_Are_Not_Included_In_AttributesToUpdate()
196197
Assert.False(attr.Key.IsImmutable);
197198
}
198199

200+
[Fact]
201+
public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship()
202+
{
203+
// arrange
204+
var contextGraphBuilder = new ContextGraphBuilder();
205+
contextGraphBuilder.AddResource<Independent>("independents");
206+
contextGraphBuilder.AddResource<Dependent>("dependents");
207+
var contextGraph = contextGraphBuilder.Build();
208+
209+
var jsonApiContextMock = new Mock<IJsonApiContext>();
210+
jsonApiContextMock.SetupAllProperties();
211+
jsonApiContextMock.Setup(m => m.ContextGraph).Returns(contextGraph);
212+
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
213+
214+
var jsonApiOptions = new JsonApiOptions();
215+
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
216+
217+
var genericProcessorFactoryMock = new Mock<IGenericProcessorFactory>();
218+
219+
var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object, genericProcessorFactoryMock.Object);
220+
221+
var property = Guid.NewGuid().ToString();
222+
var content = new Document
223+
{
224+
Data = new DocumentData
225+
{
226+
Type = "independents",
227+
Id = "1",
228+
Attributes = new Dictionary<string, object> {
229+
{ "property", property }
230+
}
231+
}
232+
};
233+
234+
var contentString = JsonConvert.SerializeObject(content);
235+
236+
// act
237+
var result = deserializer.Deserialize<Independent>(contentString);
238+
239+
// assert
240+
Assert.NotNull(result);
241+
Assert.Equal(property, result.Property);
242+
}
243+
244+
[Fact]
245+
public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_Relationship_Body()
246+
{
247+
// arrange
248+
var contextGraphBuilder = new ContextGraphBuilder();
249+
contextGraphBuilder.AddResource<Independent>("independents");
250+
contextGraphBuilder.AddResource<Dependent>("dependents");
251+
var contextGraph = contextGraphBuilder.Build();
252+
253+
var jsonApiContextMock = new Mock<IJsonApiContext>();
254+
jsonApiContextMock.SetupAllProperties();
255+
jsonApiContextMock.Setup(m => m.ContextGraph).Returns(contextGraph);
256+
jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary<AttrAttribute, object>());
257+
258+
var jsonApiOptions = new JsonApiOptions();
259+
jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions);
260+
261+
var genericProcessorFactoryMock = new Mock<IGenericProcessorFactory>();
262+
263+
var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object, genericProcessorFactoryMock.Object);
264+
265+
var property = Guid.NewGuid().ToString();
266+
var content = new Document
267+
{
268+
Data = new DocumentData
269+
{
270+
Type = "independents",
271+
Id = "1",
272+
Attributes = new Dictionary<string, object> {
273+
{ "property", property }
274+
},
275+
// a common case for this is deserialization in unit tests
276+
Relationships = new Dictionary<string, RelationshipData> {
277+
{ "dependent", new RelationshipData { } }
278+
}
279+
}
280+
};
281+
282+
var contentString = JsonConvert.SerializeObject(content);
283+
284+
// act
285+
var result = deserializer.Deserialize<Independent>(contentString);
286+
287+
// assert
288+
Assert.NotNull(result);
289+
Assert.Equal(property, result.Property);
290+
}
291+
199292
private class TestResource : Identifiable
200293
{
201294
[Attr("complex-member")]
@@ -215,5 +308,17 @@ private class ComplexType
215308
{
216309
public string CompoundName { get; set; }
217310
}
311+
312+
private class Independent : Identifiable
313+
{
314+
[Attr("property")] public string Property { get; set; }
315+
[HasOne("dependent")] public Dependent Dependent { get; set; }
316+
}
317+
318+
private class Dependent : Identifiable
319+
{
320+
[HasOne("independent")] public Independent Independent { get; set; }
321+
public int IndependentId { get; set; }
322+
}
218323
}
219324
}

0 commit comments

Comments
 (0)