private JToken SerializeData(SerializationContext serializationContext, IDictionary<string, JToken> properties)
        {
            var data = SerializeMinimalData(serializationContext, properties);

            var attributes = SerializeAttributes(serializationContext, properties);

            if (attributes != null)
            {
                data["attributes"] = attributes;
            }

            var relationships = SerializeRelationships(serializationContext, properties);

            if (relationships != null)
            {
                data["relationships"] = relationships;
            }

            if (serializationContext.IsCollection)
            {
                data["links"] = AddUrl(
                    serializationContext.BaseUrl,
                    new JObject(),
                    "self",
                    _urlBuilder.BuildCanonicalPath(serializationContext.Resource, EnsureHasId(properties, serializationContext.Resource).Value<string>()));
            }

            return data;
        }
        private JObject BuildJsonResult(ResourceResult resourceResult, string requestUri)
        {
            var serializationContext = new SerializationContext
            {
                Object = resourceResult.Result,
                Resource = resourceResult.Resource,
                IncludedSections = new JArray(),
                BaseUrl = requestUri,
                QueryContext = resourceResult.QueryContext
            };

            if (serializationContext.Object == null)
            {
                return SerializeNull(serializationContext);
            }

            var objectJson = JToken.FromObject(serializationContext.Object, _serializer);
            serializationContext.IsCollection = objectJson is JArray;

            var result = new JObject
            {
                ["data"] = SerializeArrayOrObject(serializationContext, objectJson, SerializeData),
                ["links"] = new JObject
                {
                    ["self"] = new JValue(serializationContext.BaseUrl)
                }

                ["links"] = CreateTopLevelLinks(serializationContext, serializationContext.IsCollection ? objectJson.Count() : 0)
            };

            if (serializationContext.IncludedSections.Count > 0)
            {
                result["included"] = serializationContext.IncludedSections;
            }

            return result;
        }
        private JToken CreateTopLevelLinks(SerializationContext serializationContext, int itemCount = 0)
        {
            var result = new JObject
            {
                ["self"] = serializationContext.BaseUrl
            };

            if (serializationContext.QueryContext?.Pagination != null)
            {
                var queryStrings = new PaginationQuery(serializationContext.QueryContext.Pagination);

                var left = new Uri(serializationContext.BaseUrl).GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped);

                if (queryStrings.FirstPage != null)
                {
                    result["first"] = new Uri(left + queryStrings.FirstPage);
                }

                if (queryStrings.NextPage != null && itemCount >= serializationContext.QueryContext.Pagination.PerPage)
                {
                    result["next"] = new Uri(left + queryStrings.NextPage);
                }

                if (queryStrings.PreviousPage != null)
                {
                    result["prev"] = new Uri(left + queryStrings.PreviousPage);
                }
            }

            return result;
        }
        private static JToken SerializeArrayOrObject(SerializationContext serializationContext, JToken token, Func<SerializationContext, IDictionary<string, JToken>, JToken> serializeObj)
        {
            var dataArray = token as JArray;

            // single thing, just serialize it
            if (dataArray == null)
            {
                return token is JObject ? serializeObj(serializationContext, (JObject)token) : null;
            }

            // serialize each element separately
            var data = new JArray();
            foreach (var obj in dataArray.OfType<JObject>())
            {
                data.Add(serializeObj(serializationContext, obj));
            }

            return data;
        }
 private JObject SerializeNull(SerializationContext serializationContext)
 {
     return new JObject
     {
         ["data"] = null,
         ["links"] = CreateTopLevelLinks(serializationContext)
     };
 }
 private JToken SerializeMinimalData(SerializationContext serializationContext, IDictionary<string, JToken> properties)
 {
     return SerializeMinimalData(serializationContext, properties, serializationContext.Resource);
 }
        private JToken SerializeMinimalData(SerializationContext serializationContext, IDictionary<string, JToken> properties, ApiResource resource)
        {
            var data = new JObject
            {
                ["type"] = resource.ResourceType.ToDashed(),
                ["id"] = EnsureHasId(properties, resource)
            };

            return data;
        }
 private static JToken SerializeAttributes(SerializationContext serializationContext, IDictionary<string, JToken> properties)
 {
     return SerializeAttributes(serializationContext, properties, serializationContext.Resource);
 }
        private static JToken SerializeAttributes(SerializationContext serializationContext, IDictionary<string, JToken> properties, ApiResource resource)
        {
            var attributes = new JObject();
            foreach (var attr in resource.Attributes)
            {
                var value = GetValue(attr.Name, properties);
                if (value != null)
                {
                    attributes.Add(attr.Name, value);
                }
            }

            return attributes;
        }
        private JToken GetMinimumRelationship(SerializationContext serializationContext, string id, ResourceRelationship relationship, string relationshipId)
        {
            var links = new JObject();
            AddUrl(serializationContext.BaseUrl, links, "self", _urlBuilder.BuildRelationshipPath(serializationContext.Resource, id, relationship));
            AddUrl(serializationContext.BaseUrl, links, "related", _urlBuilder.BuildRelationshipPath(serializationContext.Resource, id, relationship, relationshipId));

            return new JObject
            {
                ["links"] = links
            };
        }
 private bool IsResourceIncluded(SerializationContext context, JToken includedData)
 {
     return context.IncludedSections.Any(t =>
         t.Value<string>("type") == includedData.Value<string>("type") &&
         t.Value<string>("id") == includedData.Value<string>("id"));
 }
        private JToken GetRelationshipData(SerializationContext serializationContext, ResourceRelationship relationship, JToken relationshipValues)
        {
            var data = SerializeArrayOrObject(
                serializationContext,
                relationshipValues,
                (context, props) =>
                {
                    var values = SerializeMinimalData(context, props, relationship.RelatedResource);
                    var includedData = values.DeepClone();
                    var url = _urlBuilder.BuildCanonicalPath(
                        relationship.RelatedResource,
                        EnsureHasId(props, relationship.RelatedResource).Value<string>());

                    includedData["attributes"] = SerializeAttributes(context, props, relationship.RelatedResource);
                    includedData["links"] = AddUrl(context.BaseUrl, new JObject(), "self", url);
                    if (!IsResourceIncluded(context, includedData))
                    {
                        context.IncludedSections.Add(includedData);
                    }

                    return values;
                });
            return data;
        }
        private JToken SerializeRelationship(SerializationContext serializationContext, ResourceRelationship relationship, IDictionary<string, JToken> properties)
        {
            var relationshipValues = GetValue(relationship.Name, properties);
            var relationshipProperties = relationshipValues as JObject;

            // serialize the links part (so the data can be fetched)
            var objId = EnsureHasId(properties, serializationContext.Resource);
            var relToken = GetMinimumRelationship(
                serializationContext,
                objId.ToString(),
                relationship,
                relationshipProperties != null ? GetId(relationshipProperties, relationship.RelatedResource).Value<string>() : null);

            if (relationshipValues == null)
            {
                return relToken;
            }

            // only include data if it exists, otherwise just assume it should be fetched later
            var data = GetRelationshipData(serializationContext, relationship, relationshipValues);
            if (data != null)
            {
                relToken["data"] = data;
            }

            return relToken;
        }
        private JToken SerializeRelationships(SerializationContext serializationContext, IDictionary<string, JToken> properties)
        {
            var relationships = new JObject();

            foreach (var rel in serializationContext.Resource.Relationships)
            {
                relationships[rel.Name] = SerializeRelationship(serializationContext, rel, properties);
            }

            return relationships;
        }