diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index c3c987f32a..c2a21cb7f1 100755 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -1,6 +1,6 @@  - 2.1.9 + 2.1.10 netstandard1.6 JsonApiDotNetCore JsonApiDotNetCore diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index d8cbf245bf..25021c441e 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -164,11 +164,6 @@ private object SetHasOneRelationship(object entity, ContextEntity contextEntity, Dictionary relationships) { - var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.InternalRelationshipName}Id"); - - if (entityProperty == null) - throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.InternalRelationshipName}"); - var relationshipName = attr.PublicRelationshipName; if (relationships.TryGetValue(relationshipName, out RelationshipData relationshipData)) @@ -181,6 +176,12 @@ private object SetHasOneRelationship(object entity, if (data == null) return entity; var newValue = data["id"]; + + var foreignKey = attr.InternalRelationshipName + "Id"; + var entityProperty = entityProperties.FirstOrDefault(p => p.Name == foreignKey); + if (entityProperty == null) + throw new JsonApiException(400, $"{contextEntity.EntityType.Name} does not contain a foreign key property '{foreignKey}' for has one relationship '{attr.InternalRelationshipName}'"); + var convertedValue = TypeHelper.ConvertType(newValue, entityProperty.PropertyType); _jsonApiContext.RelationshipsToUpdate[relationshipAttr] = convertedValue; diff --git a/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs b/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs index 3e54c7b393..eeefa4d857 100644 --- a/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs +++ b/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using JsonApiDotNetCore.Builders; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Internal.Generics; @@ -196,6 +197,98 @@ public void Immutable_Attrs_Are_Not_Included_In_AttributesToUpdate() Assert.False(attr.Key.IsImmutable); } + [Fact] + public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship() + { + // arrange + var contextGraphBuilder = new ContextGraphBuilder(); + contextGraphBuilder.AddResource("independents"); + contextGraphBuilder.AddResource("dependents"); + var contextGraph = contextGraphBuilder.Build(); + + var jsonApiContextMock = new Mock(); + jsonApiContextMock.SetupAllProperties(); + jsonApiContextMock.Setup(m => m.ContextGraph).Returns(contextGraph); + jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary()); + + var jsonApiOptions = new JsonApiOptions(); + jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); + + var genericProcessorFactoryMock = new Mock(); + + var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object, genericProcessorFactoryMock.Object); + + var property = Guid.NewGuid().ToString(); + var content = new Document + { + Data = new DocumentData + { + Type = "independents", + Id = "1", + Attributes = new Dictionary { + { "property", property } + } + } + }; + + var contentString = JsonConvert.SerializeObject(content); + + // act + var result = deserializer.Deserialize(contentString); + + // assert + Assert.NotNull(result); + Assert.Equal(property, result.Property); + } + + [Fact] + public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_Relationship_Body() + { + // arrange + var contextGraphBuilder = new ContextGraphBuilder(); + contextGraphBuilder.AddResource("independents"); + contextGraphBuilder.AddResource("dependents"); + var contextGraph = contextGraphBuilder.Build(); + + var jsonApiContextMock = new Mock(); + jsonApiContextMock.SetupAllProperties(); + jsonApiContextMock.Setup(m => m.ContextGraph).Returns(contextGraph); + jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary()); + + var jsonApiOptions = new JsonApiOptions(); + jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); + + var genericProcessorFactoryMock = new Mock(); + + var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object, genericProcessorFactoryMock.Object); + + var property = Guid.NewGuid().ToString(); + var content = new Document + { + Data = new DocumentData + { + Type = "independents", + Id = "1", + Attributes = new Dictionary { + { "property", property } + }, + // a common case for this is deserialization in unit tests + Relationships = new Dictionary { + { "dependent", new RelationshipData { } } + } + } + }; + + var contentString = JsonConvert.SerializeObject(content); + + // act + var result = deserializer.Deserialize(contentString); + + // assert + Assert.NotNull(result); + Assert.Equal(property, result.Property); + } + private class TestResource : Identifiable { [Attr("complex-member")] @@ -215,5 +308,17 @@ private class ComplexType { public string CompoundName { get; set; } } + + private class Independent : Identifiable + { + [Attr("property")] public string Property { get; set; } + [HasOne("dependent")] public Dependent Dependent { get; set; } + } + + private class Dependent : Identifiable + { + [HasOne("independent")] public Independent Independent { get; set; } + public int IndependentId { get; set; } + } } }