diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index df36966b36..d289c1a788 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -252,7 +252,9 @@ private void SetHasOneForeignKeyValue(object entity, HasOneAttribute hasOneAttr, // e.g. PATCH /articles // {... { "relationships":{ "Owner": { "data": null } } } } - if (rio == null && Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) == null) + bool foreignKeyPropertyIsNullableType = Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) != null + || foreignKeyProperty.PropertyType == typeof(string); + if (rio == null && !foreignKeyPropertyIsNullableType) throw new JsonApiException(400, $"Cannot set required relationship identifier '{hasOneAttr.IdentifiablePropertyName}' to null because it is a non-nullable type."); var convertedValue = TypeHelper.ConvertType(foreignKeyPropertyValue, foreignKeyProperty.PropertyType); diff --git a/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs b/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs index f52b30c0f6..da656a9bbd 100644 --- a/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs +++ b/test/UnitTests/Serialization/JsonApiDeSerializerTests.cs @@ -222,6 +222,51 @@ public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship() Assert.Equal(property, result.Property); } + [Fact] + public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_String_Keys() + { + // arrange + var resourceGraphBuilder = new ResourceGraphBuilder(); + resourceGraphBuilder.AddResource("independents"); + resourceGraphBuilder.AddResource("dependents"); + var resourceGraph = resourceGraphBuilder.Build(); + + var jsonApiContextMock = new Mock(); + jsonApiContextMock.SetupAllProperties(); + jsonApiContextMock.Setup(m => m.ResourceGraph).Returns(resourceGraph); + jsonApiContextMock.Setup(m => m.AttributesToUpdate).Returns(new Dictionary()); + jsonApiContextMock.Setup(m => m.HasOneRelationshipPointers).Returns(new HasOneRelationshipPointers()); + + var jsonApiOptions = new JsonApiOptions(); + jsonApiContextMock.Setup(m => m.Options).Returns(jsonApiOptions); + + var deserializer = new JsonApiDeSerializer(jsonApiContextMock.Object); + + var property = Guid.NewGuid().ToString(); + var content = new Document + { + Data = new ResourceObject + { + Type = "independents", + Id = "natural-key", + Attributes = new Dictionary { { "property", property } }, + 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); + } + [Fact] public void Can_Deserialize_Independent_Side_Of_One_To_One_Relationship_With_Relationship_Body() { @@ -348,6 +393,19 @@ private class Dependent : Identifiable public int IndependentId { get; set; } } + private class IndependentWithStringKey : Identifiable + { + [Attr("property")] public string Property { get; set; } + [HasOne("dependent")] public Dependent Dependent { get; set; } + public string DependentId { get; set; } + } + + private class DependentWithStringKey : Identifiable + { + [HasOne("independent")] public Independent Independent { get; set; } + public string IndependentId { get; set; } + } + [Fact] public void Can_Deserialize_Object_With_HasManyRelationship() { @@ -538,7 +596,7 @@ public void Can_Deserialize_Nested_Included_HasMany_Relationships() resourceGraphBuilder.AddResource("many-to-manys"); var deserializer = GetDeserializer(resourceGraphBuilder); - + var contentString = @"{ ""data"": {