Skip to content

Commit 8a8b07d

Browse files
committed
Issue #101, nullable embedded lists
1 parent cd3ac5a commit 8a8b07d

5 files changed

Lines changed: 76 additions & 30 deletions

File tree

WebApi.Hal.Tests/HalResourceMixedContentTest.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System.Collections.Generic;
2+
using System.IO;
23
using System.Linq;
34
using System.Net.Http;
45
using System.Text;
@@ -19,6 +20,7 @@ public HalResourceMixedContentTest()
1920
{
2021
Boss = new Boss(2, "Eunice PHB", 1, true)
2122
};
23+
resource.People = new List<Person>();
2224
resource.People.Add(new Person(3, "Dilbert", 1));
2325
resource.People.Add(new Person(4, "Wally", 1));
2426
resource.People.Add(new Person(5, "Alice", 1));
@@ -214,6 +216,35 @@ public void peopledetail_post_json_embedded_arrays_test()
214216
}
215217
}
216218

219+
[Fact]
220+
public void peopledetail_post_json_embedded_null_test()
221+
{
222+
// arrange
223+
var mediaFormatter = new JsonHalMediaTypeFormatter { Indent = true };
224+
var type = typeof(OrganisationWithPeopleDetailRepresentation);
225+
const string json = @"
226+
{
227+
""Id"":""3"",
228+
""Name"": ""Singles Dept.""
229+
}
230+
";
231+
232+
// act
233+
using (
234+
var stream = new MemoryStream(Encoding.UTF8.GetBytes(json))
235+
)
236+
{
237+
var obj = mediaFormatter.ReadFromStreamAsync(type, stream, null, null).Result;
238+
239+
// assert
240+
Assert.NotNull(obj);
241+
var org = obj as OrganisationWithPeopleDetailRepresentation;
242+
Assert.NotNull(org);
243+
Assert.Null(org.Boss);
244+
Assert.Null(org.People);
245+
}
246+
}
247+
217248
class MySimpleList : SimpleListRepresentation<OrganisationRepresentation>
218249
{
219250
protected override void CreateHypermedia()

WebApi.Hal.Tests/Representations/OrganisationWithPeopleDetailRepresentation.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,12 @@ public OrganisationWithPeopleDetailRepresentation(int id, string name)
6464
{
6565
Id = id;
6666
Name = name;
67-
People = new List<Person>();
6867
}
6968

7069
public int Id { get; set; }
7170
public string Name { get; set; }
7271

73-
public List<Person> People { get; set; }
72+
public IList<Person> People { get; set; }
7473
public Boss Boss { get; set; }
7574

7675
public override string Rel

WebApi.Hal.Web/Api/BeerDetailController.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System.Collections.Generic;
2+
using System.Linq;
23
using System.Web.Http;
34
using WebApi.Hal.Web.Api.Resources;
45
using WebApi.Hal.Web.Data;
@@ -37,8 +38,12 @@ public BeerDetailRepresentation Get(int id)
3738
Style = new BeerStyleRepresentation {Id = beer.Style.Id, Name = beer.Style.Name},
3839
Brewery = new BreweryRepresentation {Id = beer.Brewery.Id, Name = beer.Brewery.Name}
3940
};
40-
foreach (var review in reviews)
41-
detail.Reviews.Add(review);
41+
if (reviews.Count > 0)
42+
{
43+
detail.Reviews = new List<ReviewRepresentation>();
44+
foreach (var review in reviews)
45+
detail.Reviews.Add(review);
46+
}
4247
return detail;
4348
}
4449

WebApi.Hal.Web/Api/Resources/BeerDetailRepresentation.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@ namespace WebApi.Hal.Web.Api.Resources
44
{
55
public class BeerDetailRepresentation : Representation
66
{
7-
public BeerDetailRepresentation()
8-
{
9-
Reviews = new List<ReviewRepresentation>();
10-
}
11-
127
public int Id { get; set; }
138
public string Name { get; set; }
149

WebApi.Hal/JsonConverters/ResourceConverter.cs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,27 @@ static IResource CreateResource(JObject jObj, Type resourceType)
9595
foreach (var prop in resourceType.GetProperties().Where(p => Representation.IsEmbeddedResourceType(p.PropertyType)))
9696
{
9797
// expects embedded collection of resources is implemented as an IList on the Representation-derived class
98-
var lst = prop.GetValue(resource) as IList;
99-
if (lst != null)
98+
if (typeof (IEnumerable<IResource>).IsAssignableFrom(prop.PropertyType))
10099
{
100+
var lst = prop.GetValue(resource) as IList;
101+
if (lst == null)
102+
{
103+
lst = ConstructResource(prop.PropertyType) as IList ??
104+
Activator.CreateInstance(
105+
typeof (List<>).MakeGenericType(prop.PropertyType.GenericTypeArguments)) as IList;
106+
if (lst == null) continue;
107+
prop.SetValue(resource, lst);
108+
}
101109
if (prop.PropertyType.GenericTypeArguments != null &&
102110
prop.PropertyType.GenericTypeArguments.Length > 0)
103111
CreateEmbedded(embeddeds, prop.PropertyType.GenericTypeArguments[0],
104112
newRes => lst.Add(newRes));
105113
}
106114
else
107-
CreateEmbedded(embeddeds, prop.PropertyType, newRes => prop.SetValue(resource, newRes));
115+
{
116+
var prop1 = prop;
117+
CreateEmbedded(embeddeds, prop.PropertyType, newRes => prop1.SetValue(resource, newRes));
118+
}
108119
}
109120
}
110121

@@ -177,22 +188,7 @@ static string GetResourceTypeRel(Type resourceType)
177188
{
178189
if (ResourceTypeToRel.ContainsKey(resourceType.FullName))
179190
return ResourceTypeToRel[resourceType.FullName];
180-
// favor c-tor with zero params, but if it doesn't exist, use c-tor with fewest params and pass all null values
181-
var ctors = resourceType.GetConstructors();
182-
ConstructorInfo useThisCtor = null;
183-
foreach (var ctor in ctors)
184-
{
185-
if (ctor.GetParameters().Length == 0)
186-
{
187-
useThisCtor = ctor;
188-
break;
189-
}
190-
if (useThisCtor == null || useThisCtor.GetParameters().Length > ctor.GetParameters().Length)
191-
useThisCtor = ctor;
192-
}
193-
if (useThisCtor == null) return string.Empty;
194-
var ctorParams = new object[useThisCtor.GetParameters().Length];
195-
var res = useThisCtor.Invoke(ctorParams) as IResource;
191+
var res = ConstructResource(resourceType) as IResource;
196192
if (res != null)
197193
{
198194
var rel = res.Rel;
@@ -208,6 +204,26 @@ static string GetResourceTypeRel(Type resourceType)
208204
}
209205
}
210206

207+
static object ConstructResource(Type resourceType)
208+
{
209+
// favor c-tor with zero params, but if it doesn't exist, use c-tor with fewest params and pass all null values
210+
var ctors = resourceType.GetConstructors();
211+
ConstructorInfo useThisCtor = null;
212+
foreach (var ctor in ctors)
213+
{
214+
if (ctor.GetParameters().Length == 0)
215+
{
216+
useThisCtor = ctor;
217+
break;
218+
}
219+
if (useThisCtor == null || useThisCtor.GetParameters().Length > ctor.GetParameters().Length)
220+
useThisCtor = ctor;
221+
}
222+
if (useThisCtor == null) return null;
223+
var ctorParams = new object[useThisCtor.GetParameters().Length];
224+
return useThisCtor.Invoke(ctorParams);
225+
}
226+
211227
public override bool CanConvert(Type objectType)
212228
{
213229
return IsResource(objectType) && !IsResourceList(objectType);

0 commit comments

Comments
 (0)