Skip to content

Commit 774f982

Browse files
author
GlennGeelen
committed
Update json format as defined in JSON API v1
- rename links to relationships - add a data object or array to the relationships
1 parent 70e220a commit 774f982

1 file changed

Lines changed: 115 additions & 81 deletions

File tree

JSONAPI/Json/JsonApiFormatter.cs

Lines changed: 115 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -198,91 +198,32 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
198198
writer.WriteStartObject();
199199

200200
var resourceType = value.GetType();
201-
202-
// Write the type
203-
writer.WritePropertyName("type");
204201
var jsonTypeKey = _modelManager.GetResourceTypeNameForType(resourceType);
205-
writer.WriteValue(jsonTypeKey);
206-
207-
// Do the Id now...
208-
writer.WritePropertyName("id");
209202
var idProp = _modelManager.GetIdProperty(resourceType);
210-
writer.WriteValue(GetValueForIdProperty(idProp, value));
203+
204+
// Write the type and id
205+
WriteTypeAndId(writer, resourceType, value);
211206

212207
// Leverage the cached map to avoid another costly call to System.Type.GetProperties()
213208
var props = _modelManager.GetProperties(value.GetType());
214209

215-
// Do non-model properties first, everything else goes in "links"
210+
// Do non-model properties first, everything else goes in "related"
216211
//TODO: Unless embedded???
217212
var relationshipModelProperties = new List<RelationshipModelProperty>();
218213

219-
foreach (var modelProperty in props)
220-
{
221-
var prop = modelProperty.Property;
222-
if (prop == idProp) continue; // Don't write the "id" property twice, see above!
223-
224-
if (modelProperty is FieldModelProperty)
225-
{
226-
if (modelProperty.IgnoreByDefault) continue; // TODO: allow overriding this
227-
228-
// numbers, strings, dates...
229-
writer.WritePropertyName(modelProperty.JsonKey);
230-
231-
var propertyValue = prop.GetValue(value, null);
232-
233-
if (prop.PropertyType == typeof (Decimal) || prop.PropertyType == typeof (Decimal?))
234-
{
235-
if (propertyValue == null)
236-
writer.WriteNull();
237-
else
238-
writer.WriteValue(propertyValue.ToString());
239-
}
240-
else if (prop.PropertyType == typeof (string) &&
241-
prop.GetCustomAttributes().Any(attr => attr is SerializeStringAsRawJsonAttribute))
242-
{
243-
if (propertyValue == null)
244-
{
245-
writer.WriteNull();
246-
}
247-
else
248-
{
249-
var json = (string) propertyValue;
250-
if (ValidateRawJsonStrings)
251-
{
252-
try
253-
{
254-
var token = JToken.Parse(json);
255-
json = token.ToString();
256-
}
257-
catch (Exception)
258-
{
259-
json = "{}";
260-
}
261-
}
262-
var valueToSerialize = JsonHelpers.MinifyJson(json);
263-
writer.WriteRawValue(valueToSerialize);
264-
}
265-
}
266-
else
267-
{
268-
serializer.Serialize(writer, propertyValue);
269-
}
270-
}
271-
else if (modelProperty is RelationshipModelProperty)
272-
{
273-
relationshipModelProperties.Add((RelationshipModelProperty)modelProperty);
274-
}
275-
}
214+
// Write attributes
215+
WriteAttributes(props, writer, idProp, value, serializer, relationshipModelProperties);
276216

277217
// Now do other stuff
278218
if (relationshipModelProperties.Count() > 0)
279219
{
280-
writer.WritePropertyName("links");
220+
writer.WritePropertyName("relastionships");
281221
writer.WriteStartObject();
282222
}
283223
foreach (var relationshipModelProperty in relationshipModelProperties)
284224
{
285-
bool skip = false, iip = false;
225+
bool skip = false,
226+
iip = false;
286227
string lt = null;
287228
SerializeAsOptions sa = SerializeAsOptions.Ids;
288229

@@ -324,11 +265,12 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
324265
switch (sa)
325266
{
326267
case SerializeAsOptions.Ids:
327-
//writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
328268
IEnumerable<object> items = (IEnumerable<object>)prop.GetValue(value, null);
329269
if (items == null)
330270
{
331-
writer.WriteValue((IEnumerable<object>)null); //TODO: Is it okay with the spec and Ember Data to return null for an empty array?
271+
// Return an empty array when there are no items
272+
writer.WriteStartArray();
273+
writer.WriteEndArray();
332274
break; // LOOK OUT! Ending this case block early here!!!
333275
}
334276
this.WriteIdsArrayJson(writer, items, serializer);
@@ -354,7 +296,7 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
354296
//writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
355297
//TODO: Support ids and type properties in "link" object
356298
writer.WriteStartObject();
357-
writer.WritePropertyName("related");
299+
writer.WritePropertyName("links");
358300
writer.WriteValue(href);
359301
writer.WriteEndObject();
360302
break;
@@ -383,8 +325,15 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
383325
switch (sa)
384326
{
385327
case SerializeAsOptions.Ids:
386-
//writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
387-
serializer.Serialize(writer, objId.Value);
328+
// Write the data element
329+
writer.WriteStartObject();
330+
writer.WritePropertyName(PrimaryDataKeyName);
331+
writer.WriteStartObject();
332+
// Write the type and id
333+
WriteTypeAndId(writer, prop.PropertyType, propertyValue);
334+
335+
writer.WriteEndObject();
336+
writer.WriteEndObject();
388337
if (iip)
389338
if (aggregator != null)
390339
aggregator.Add(prop.PropertyType, propertyValue);
@@ -398,7 +347,7 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
398347

399348
//writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
400349
writer.WriteStartObject();
401-
writer.WritePropertyName("related");
350+
writer.WritePropertyName("links");
402351
writer.WriteValue(link);
403352
writer.WriteEndObject();
404353
break;
@@ -492,7 +441,7 @@ protected void SerializeLinkedResources(Stream writeStream, JsonWriter writer, J
492441

493442
if (aggregator.Appendices.Count > 0)
494443
{
495-
writer.WritePropertyName("linked");
444+
writer.WritePropertyName("included");
496445
writer.WriteStartObject();
497446

498447
// Okay, we should have captured everything now. Now combine the type writers into the main writer...
@@ -509,6 +458,76 @@ protected void SerializeLinkedResources(Stream writeStream, JsonWriter writer, J
509458

510459
}
511460

461+
private void WriteAttributes(ModelProperty[] props, JsonWriter writer, PropertyInfo idProp, object value, JsonSerializer serializer, List<RelationshipModelProperty> relationshipModelProperties)
462+
{
463+
//if (props.Count() > 0)
464+
//{
465+
// writer.WritePropertyName("attributes");
466+
// writer.WriteStartObject();
467+
//}
468+
469+
foreach (var modelProperty in props)
470+
{
471+
var prop = modelProperty.Property;
472+
if (prop == idProp) continue; // Don't write the "id" property twice, see above!
473+
474+
if (modelProperty is FieldModelProperty)
475+
{
476+
if (modelProperty.IgnoreByDefault) continue; // TODO: allow overriding this
477+
478+
// numbers, strings, dates...
479+
writer.WritePropertyName(modelProperty.JsonKey);
480+
481+
var propertyValue = prop.GetValue(value, null);
482+
483+
if (prop.PropertyType == typeof(Decimal) || prop.PropertyType == typeof(Decimal?))
484+
{
485+
if (propertyValue == null)
486+
writer.WriteNull();
487+
else
488+
writer.WriteValue(propertyValue);
489+
}
490+
else if (prop.PropertyType == typeof(string) &&
491+
prop.GetCustomAttributes().Any(attr => attr is SerializeStringAsRawJsonAttribute))
492+
{
493+
if (propertyValue == null)
494+
{
495+
writer.WriteNull();
496+
}
497+
else
498+
{
499+
var json = (string)propertyValue;
500+
if (ValidateRawJsonStrings)
501+
{
502+
try
503+
{
504+
var token = JToken.Parse(json);
505+
json = token.ToString();
506+
}
507+
catch (Exception)
508+
{
509+
json = "{}";
510+
}
511+
}
512+
var valueToSerialize = JsonHelpers.MinifyJson(json);
513+
writer.WriteRawValue(valueToSerialize);
514+
}
515+
}
516+
else
517+
{
518+
serializer.Serialize(writer, propertyValue);
519+
}
520+
}
521+
else if (modelProperty is RelationshipModelProperty)
522+
{
523+
relationshipModelProperties.Add((RelationshipModelProperty)modelProperty);
524+
}
525+
}
526+
527+
//if (props.Count() > 0)
528+
// writer.WriteEndObject();
529+
}
530+
512531
#endregion Serialization
513532

514533
#region Deserialization
@@ -558,11 +577,11 @@ private object ReadFromStream(Type type, Stream readStream, HttpContent content,
558577
reader.Read(); // burn the PropertyName token
559578
switch (value)
560579
{
561-
case "linked":
580+
case "included":
562581
//TODO: If we want to capture linked/related objects in a compound document when deserializing, do it here...do we?
563582
reader.Skip();
564583
break;
565-
case "links":
584+
case "relationships":
566585
// ignore this, is it even meaningful in a PUT/POST body?
567586
reader.Skip();
568587
break;
@@ -712,7 +731,7 @@ private object Deserialize(Type objectType, JsonReader reader)
712731
string value = (string)reader.Value;
713732
var modelProperty = _modelManager.GetPropertyForJsonKey(objectType, value);
714733

715-
if (value == "links")
734+
if (value == "related")
716735
{
717736
reader.Read(); // burn the PropertyName token
718737
//TODO: linked resources (Done??)
@@ -724,7 +743,7 @@ private object Deserialize(Type objectType, JsonReader reader)
724743
{
725744
reader.Read(); // burn the PropertyName token
726745
//TODO: Embedded would be dropped here!
727-
continue; // These aren't supposed to be here, they're supposed to be in "links"!
746+
continue; // These aren't supposed to be here, they're supposed to be in "related"!
728747
}
729748

730749
var prop = modelProperty.Property;
@@ -1008,16 +1027,31 @@ protected string GetIdFor(object obj)
10081027
return GetValueForIdProperty(idprop, obj);
10091028
}
10101029

1030+
private void WriteTypeAndId(JsonWriter writer, Type propertyType, object propertyValue)
1031+
{
1032+
writer.WritePropertyName("type");
1033+
writer.WriteValue(_modelManager.GetResourceTypeNameForType(propertyType));
1034+
1035+
writer.WritePropertyName("id");
1036+
writer.WriteValue(GetValueForIdProperty(_modelManager.GetIdProperty(propertyType), propertyValue));
1037+
}
1038+
10111039
private void WriteIdsArrayJson(Newtonsoft.Json.JsonWriter writer, IEnumerable<object> value, Newtonsoft.Json.JsonSerializer serializer)
10121040
{
1041+
// Write the data element
1042+
writer.WriteStartObject();
1043+
writer.WritePropertyName(PrimaryDataKeyName);
1044+
10131045
IEnumerator<Object> collectionEnumerator = (value as IEnumerable<object>).GetEnumerator();
10141046
writer.WriteStartArray();
10151047
while (collectionEnumerator.MoveNext())
10161048
{
1017-
var serializable = collectionEnumerator.Current;
1018-
writer.WriteValue(this.GetIdFor(serializable));
1049+
writer.WriteStartObject();
1050+
WriteTypeAndId(writer, collectionEnumerator.Current.GetType(), collectionEnumerator.Current);
1051+
writer.WriteEndObject();
10191052
}
10201053
writer.WriteEndArray();
1054+
writer.WriteEndObject();
10211055
}
10221056

10231057
}

0 commit comments

Comments
 (0)