Skip to content

Commit b1ed2ab

Browse files
committed
feat(serialization): add output formatter
1 parent 2fdae77 commit b1ed2ab

File tree

18 files changed

+266
-14
lines changed

18 files changed

+266
-14
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Internal;
3+
using JsonApiDotNetCore.Models;
4+
5+
namespace JsonApiDotNetCore.Builders
6+
{
7+
public class DocumentBuilder
8+
{
9+
private IContextGraph _contextGraph;
10+
11+
public DocumentBuilder(IContextGraph contextGraph)
12+
{
13+
_contextGraph = contextGraph;
14+
}
15+
16+
public Document Build(IIdentifiable entity)
17+
{
18+
var contextEntity = _contextGraph.GetContextEntity(entity.GetType());
19+
20+
var document = new Document
21+
{
22+
Data = _getData(contextEntity, entity)
23+
};
24+
25+
return document;
26+
}
27+
28+
public Documents Build(IEnumerable<IIdentifiable> entities)
29+
{
30+
var entityType = entities
31+
.GetType()
32+
.GenericTypeArguments[0];
33+
34+
var contextEntity = _contextGraph.GetContextEntity(entityType);
35+
36+
var documents = new Documents
37+
{
38+
Data = new List<DocumentData>()
39+
};
40+
41+
foreach (var entity in entities)
42+
documents.Data.Add(_getData(contextEntity, entity));
43+
44+
return documents;
45+
}
46+
47+
private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
48+
{
49+
var data = new DocumentData
50+
{
51+
Type = contextEntity.EntityName,
52+
Id = entity.Id.ToString(),
53+
Attributes = new Dictionary<string, object>()
54+
};
55+
56+
contextEntity.Attributes.ForEach(attr =>
57+
{
58+
data.Attributes.Add(attr.PublicAttributeName, attr.GetValue(entity));
59+
});
60+
61+
return data;
62+
}
63+
}
64+
}

src/JsonApiDotNetCore/Controllers/JsonApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace JsonApiDotNetCore.Controllers
1111
{
12-
public class JsonApiController<T> : Controller where T : class, IIdentifiable
12+
public class JsonApiController<T> : Controller where T : class, IIdentifiable<int>
1313
{
1414
private readonly DbContext _context;
1515
private readonly DbSet<T> _dbSet;

src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using JsonApiDotNetCore.Formatters;
12
using JsonApiDotNetCore.Internal;
23
using JsonApiDotNetCore.Services;
4+
using Microsoft.AspNetCore.Mvc;
35
using Microsoft.EntityFrameworkCore;
46
using Microsoft.Extensions.DependencyInjection;
57

@@ -8,6 +10,14 @@ namespace JsonApiDotNetCore.Extensions
810
public static class ServiceProviderExtensions
911
{
1012
public static void AddJsonApi<T>(this IServiceCollection services) where T : DbContext
13+
{
14+
services.AddJsonApiInternals<T>();
15+
16+
services.AddMvc()
17+
.AddMvcOptions(options => options.SerializeAsJsonApi());
18+
}
19+
20+
public static void AddJsonApiInternals<T>(this IServiceCollection services) where T : DbContext
1121
{
1222
var contextGraphBuilder = new ContextGraphBuilder<T>();
1323
var contextGraph = contextGraphBuilder.Build();
@@ -17,5 +27,12 @@ public static void AddJsonApi<T>(this IServiceCollection services) where T : DbC
1727

1828
services.AddSingleton<IJsonApiContext>(jsonApiContext);
1929
}
30+
31+
public static void SerializeAsJsonApi(this MvcOptions options)
32+
{
33+
options.InputFormatters.Insert(0, new JsonApiInputFormatter());
34+
35+
options.OutputFormatters.Insert(0, new JsonApiOutputFormatter());
36+
}
2037
}
2138
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Text;
2+
3+
namespace JsonApiDotNetCore.Extensions
4+
{
5+
public static class StringExtensions
6+
{
7+
public static string Dasherize(this string str)
8+
{
9+
var chars = str.ToCharArray();
10+
if (chars.Length > 0)
11+
{
12+
var builder = new StringBuilder();
13+
for (var i = 0; i < chars.Length; i++)
14+
{
15+
if (char.IsUpper(chars[i]))
16+
{
17+
var hashedString = (i > 0) ? $"-{char.ToLower(chars[i])}" : $"{char.ToLower(chars[i])}";
18+
builder.Append(hashedString);
19+
}
20+
else
21+
{
22+
builder.Append(chars[i]);
23+
}
24+
}
25+
return builder.ToString();
26+
}
27+
return str;
28+
}
29+
}
30+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Text;
3+
using System.Threading.Tasks;
4+
using JsonApiDotNetCore.Internal;
5+
using JsonApiDotNetCore.Serialization;
6+
using JsonApiDotNetCore.Services;
7+
using Microsoft.AspNetCore.Mvc.Formatters;
8+
using Microsoft.Extensions.DependencyInjection;
9+
10+
namespace JsonApiDotNetCore.Formatters
11+
{
12+
public class JsonApiOutputFormatter : IOutputFormatter
13+
{
14+
public bool CanWriteResult(OutputFormatterCanWriteContext context)
15+
{
16+
if (context == null)
17+
throw new ArgumentNullException(nameof(context));
18+
19+
var contentTypeString = context.HttpContext.Request.ContentType;
20+
21+
return string.IsNullOrEmpty(contentTypeString) || contentTypeString == "application/vnd.api+json";
22+
}
23+
24+
public async Task WriteAsync(OutputFormatterWriteContext context)
25+
{
26+
if (context == null)
27+
throw new ArgumentNullException(nameof(context));
28+
29+
var response = context.HttpContext.Response;
30+
31+
using (var writer = context.WriterFactory(response.Body, Encoding.UTF8))
32+
{
33+
var contextGraph = context.HttpContext.RequestServices.GetService<IJsonApiContext>().ContextGraph;
34+
35+
var responseContent = JsonApiSerializer.Serialize(context.Object, contextGraph);
36+
37+
await writer.WriteAsync(responseContent);
38+
39+
await writer.FlushAsync();
40+
}
41+
}
42+
}
43+
}

src/JsonApiDotNetCore/Internal/AttrAttribute.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Reflection;
23

34
namespace JsonApiDotNetCore.Internal
45
{
@@ -11,5 +12,13 @@ public AttrAttribute(string publicName)
1112

1213
public string PublicAttributeName { get; set; }
1314
public string InternalAttributeName { get; set; }
15+
16+
public object GetValue(object entity)
17+
{
18+
return entity
19+
.GetType()
20+
.GetProperty(InternalAttributeName)
21+
.GetValue(entity);
22+
}
1423
}
1524
}

src/JsonApiDotNetCore/Internal/ContextGraph.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using Microsoft.EntityFrameworkCore;
5+
using System;
56

67
namespace JsonApiDotNetCore.Internal
78
{
@@ -16,6 +17,13 @@ public ContextEntity GetContextEntity(string dbSetName)
1617
e.EntityName.ToLower() == dbSetName.ToLower());
1718
}
1819

20+
public ContextEntity GetContextEntity(Type entityType)
21+
{
22+
return Entities
23+
.FirstOrDefault(e =>
24+
e.EntityType == entityType);
25+
}
26+
1927
public object GetRelationship<TParent>(TParent entity, string relationshipName)
2028
{
2129
var parentEntityType = typeof(TParent);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
using System;
2+
13
namespace JsonApiDotNetCore.Internal
24
{
35
public interface IContextGraph
46
{
57
object GetRelationship<TParent>(TParent entity, string relationshipName);
68
string GetRelationshipName<TParent>(string relationshipName);
79
ContextEntity GetContextEntity(string dbSetName);
10+
ContextEntity GetContextEntity(Type entityType);
811
}
912
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
using Newtonsoft.Json;
2+
13
namespace JsonApiDotNetCore.Models
24
{
35
public class Document
46
{
7+
[JsonProperty("data")]
58
public DocumentData Data { get; set; }
69
}
710
}
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Extensions;
3+
using Newtonsoft.Json;
24

35
namespace JsonApiDotNetCore.Models
46
{
57
public class DocumentData
68
{
7-
public string Type { get; set; }
8-
public string Id { get; set; }
9-
public Dictionary<string,object> Attributes { get; set; }
9+
private string _type;
10+
11+
[JsonProperty("type")]
12+
public string Type
13+
{
14+
get { return _type; }
15+
set { _type = value.Dasherize(); }
16+
}
17+
18+
[JsonProperty("id")]
19+
public string Id { get; set; }
20+
21+
[JsonProperty("attributes")]
22+
public Dictionary<string, object> Attributes { get; set; }
1023
}
1124
}

0 commit comments

Comments
 (0)