Exemplo n.º 1
0
        private void WriteExplicitIdentifierJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var serializationData = SerializationData.GetSerializationData(writer);

            var valueType = value.GetType();
            var resourceIdentifierContract = (ResourceIdentifierContract)serializer.ContractResolver.ResolveContract(valueType);
            var resourceObject             = resourceIdentifierContract.ResourceObjectProperty.ValueProvider.GetValue(value);

            if (resourceObject == null)
            {
                writer.WriteNull();
                return;
            }

            var resourceObjectType     = resourceObject.GetType();
            var resourceObjectContract = (ResourceObjectContract)serializer.ContractResolver.ResolveContract(resourceObjectType);

            writer.WriteStartObject();

            //A "resource identifier object" MUST contain type and id members.
            //serialize id
            WriterUtil.ShouldWriteStringProperty(writer, resourceObject, resourceObjectContract.IdProperty, serializer, out string id);
            writer.WritePropertyName(PropertyNames.Id);
            writer.WriteValue(id);

            //serialize type. Will always out put a type
            WriterUtil.ShouldWriteStringProperty(writer, resourceObject, resourceObjectContract.TypeProperty, serializer, out string type);
            type = type ?? WriterUtil.CalculateDefaultJsonApiType(resourceObject, serializationData, serializer);
            writer.WritePropertyName(PropertyNames.Type);
            writer.WriteValue(type);

            for (var i = 0; i < resourceIdentifierContract.Properties.Count; i++)
            {
                var resourceIdentifierProp = resourceIdentifierContract.Properties[i];
                if (resourceIdentifierProp == resourceIdentifierContract.ResourceObjectProperty)
                {
                    continue;
                }
                switch (resourceIdentifierProp.PropertyName)
                {
                case PropertyNames.Id:
                case PropertyNames.Type:
                    break;

                default:
                    if (WriterUtil.ShouldWriteProperty(value, resourceIdentifierProp, serializer, out object propValue))
                    {
                        writer.WritePropertyName(resourceIdentifierProp.PropertyName);
                        serializer.Serialize(writer, propValue);
                    }
                    break;
                }
            }

            writer.WriteEndObject();

            var reference = new ResourceObjectReference(id, type);

            serializationData.Included[reference] = resourceObject;
        }
Exemplo n.º 2
0
        private void WriteResourceObjectJson(JsonWriter writer, object resourceObject, JsonSerializer serializer)
        {
            if (resourceObject == null)
            {
                writer.WriteNull();
                return;
            }

            var serializationData = SerializationData.GetSerializationData(writer);

            var resourceObjectType     = resourceObject.GetType();
            var resourceObjectContract = (ResourceObjectContract)serializer.ContractResolver.ResolveContract(resourceObjectType);

            writer.WriteStartObject();

            //A "resource identifier object" MUST contain type and id members.
            //serialize id
            WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.IdProperty, serializer, out string id);
            writer.WritePropertyName(PropertyNames.Id);
            writer.WriteValue(id);

            //serialize type. Will always out put a type
            WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.TypeProperty, serializer, out string type);

            type = type ?? WriterUtil.CalculateDefaultJsonApiType(resourceObject, serializationData, serializer);
            writer.WritePropertyName(PropertyNames.Type);
            writer.WriteValue(type);

            //we will only write the object to included if there are properties that have have data
            //that we cant include within the reference
            var willWriteObjectToIncluded = WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.LinksProperty, serializer, out object _);

            for (var i = 0; i < resourceObjectContract.Attributes.Length && !willWriteObjectToIncluded; i++)
            {
                willWriteObjectToIncluded = WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.Attributes[i], serializer, out object _);
            }
            for (var i = 0; i < resourceObjectContract.Relationships.Length && !willWriteObjectToIncluded; i++)
            {
                willWriteObjectToIncluded = WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.Relationships[i], serializer, out object _);
            }

            // typically we would just write the meta in the included. But if we are not going to
            // have something in included we will write the meta inline here
            if (!willWriteObjectToIncluded && WriterUtil.ShouldWriteProperty(resourceObject, resourceObjectContract.MetaProperty, serializer, out object metaVal))
            {
                writer.WritePropertyName(PropertyNames.Meta);
                serializer.Serialize(writer, metaVal);
            }

            writer.WriteEndObject();

            if (willWriteObjectToIncluded)
            {
                var reference = new ResourceObjectReference(id, type);
                serializationData.Included[reference] = resourceObject;
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Forks the reader and reads ahead to find the id and type of an object reference
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns></returns>
        public static ResourceObjectReference ReadAheadToIdentifyObject(ForkableJsonReader reader)
        {
            var lookAheadReader = reader.Fork();
            var reference       = new ResourceObjectReference();

            foreach (var propName in ReaderUtil.IterateProperties(lookAheadReader))
            {
                if (propName == PropertyNames.Id)
                {
                    if (lookAheadReader.TokenType != JsonToken.String)
                    {
                        throw new JsonApiFormatException(lookAheadReader.FullPath,
                                                         $"Expected to find string at {lookAheadReader.FullPath}",
                                                         "The value of 'id' MUST be a string");
                    }
                    reference.Id = (string)lookAheadReader.Value;
                }

                else if (propName == PropertyNames.Type)
                {
                    if (lookAheadReader.TokenType != JsonToken.String)
                    {
                        throw new JsonApiFormatException(lookAheadReader.FullPath,
                                                         $"Expected to find string at {lookAheadReader.FullPath}",
                                                         "The value of 'type' MUST be a string");
                    }
                    reference.Type = (string)lookAheadReader.Value;
                }


                if (reference.Id != null && reference.Type != null)
                {
                    break;
                }
            }

            return(reference);
        }
Exemplo n.º 4
0
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var serializationData = SerializationData.GetSerializationData(writer);

            if (!serializationData.HasProcessedDocumentRoot)
            {
                //treat this value as a document root
                DocumentRootConverter.ResolveAsRootData(writer, value, serializer);
                return;
            }

            // if they have custom convertors registered, we will respect them
            if (WriterUtil.TryUseCustomConvertor(writer, value, serializer, excludeConverter: this))
            {
                return;
            }

            var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver;
            var valueType = value.GetType();

            if (!(serializer.ContractResolver.ResolveContract(valueType) is ResourceObjectContract metadata))
            {
                throw new JsonApiFormatException(
                          writer.Path,
                          $"Expected to find to find resource object, but found '{value}'",
                          "Resource indentifier objects MUST contain 'id' members");
            }

            serializationData.ConverterStack.Push(this);

            writer.WriteStartObject();

            //serialize id
            string id = null;

            if (WriterUtil.ShouldWriteProperty(value, metadata.IdProperty, serializer, out object objId))
            {
                //we will allow some non-string properties if it is trival to convert to a string
                if (!AllowedIdTypes.Contains(metadata.IdProperty.PropertyType))
                {
                    throw new JsonApiFormatException(
                              writer.Path,
                              $"Expected Id property to be a string or primitive but found it to be '{objId?.GetType()}'",
                              "The values of the id member MUST be a string");
                }

                id = objId as string ?? objId?.ToString();

                writer.WritePropertyName(PropertyNames.Id);
                writer.WriteValue(id);
            }

            //serialize type. Will always out put a type
            WriterUtil.ShouldWriteProperty <string>(value, metadata.TypeProperty, serializer, out string type);
            type = type ?? WriterUtil.CalculateDefaultJsonApiType(value, serializationData, serializer);
            writer.WritePropertyName(PropertyNames.Type);
            writer.WriteValue(type);

            //serialize links
            if (WriterUtil.ShouldWriteProperty(value, metadata.LinksProperty, serializer, out object links))
            {
                writer.WritePropertyName(PropertyNames.Links);
                serializer.Serialize(writer, links);
            }

            //serialize meta
            if (WriterUtil.ShouldWriteProperty(value, metadata.MetaProperty, serializer, out object meta))
            {
                writer.WritePropertyName(PropertyNames.Meta);
                serializer.Serialize(writer, meta);
            }

            // store all the relationships, that appear to be attributes from the
            // property declared type, types but the runtime type shows they are
            // actaully relationships
            List <KeyValuePair <JsonProperty, object> > undeclaredRelationships = null;

            //serialize attributes
            var startedAttributeSection = false;

            for (var i = 0; i < metadata.Attributes.Length; i++)
            {
                var attributeProperty = metadata.Attributes[i];
                if (WriterUtil.ShouldWriteProperty(value, attributeProperty, serializer, out object attributeValue))
                {
                    // some relationships are not decalred as such. They exist in properties
                    // with declared types of `object` but the runtime object within is a
                    // relationship. We will check here if this attribute property is really
                    // a relationship, and if it is store it to process later

                    // NOTE: this behviour it leads to nulls being inconsistantly attribute/relationship.
                    // leaving in for backward compatability but remove on next breaking change
                    var attributeValueType = attributeValue?.GetType();
                    if (attributeValueType != null &&
                        attributeProperty.PropertyType != attributeValueType &&
                        jsonApiContractResolver.ResourceRelationshipConverter.CanConvert(attributeValueType))
                    {
                        undeclaredRelationships = undeclaredRelationships ?? new List <KeyValuePair <JsonProperty, object> >();
                        undeclaredRelationships.Add(new KeyValuePair <JsonProperty, object>(attributeProperty, attributeValue));
                        continue;
                    }

                    //serialize out the attribute
                    if (!startedAttributeSection)
                    {
                        startedAttributeSection = true;
                        writer.WritePropertyName(PropertyNames.Attributes);
                        writer.WriteStartObject();
                    }
                    writer.WritePropertyName(attributeProperty.PropertyName);
                    if (attributeProperty.MemberConverter?.CanWrite == true)
                    {
                        attributeProperty.MemberConverter.WriteJson(writer, attributeValue, serializer);
                    }
                    else if (attributeValue is string attributeString)
                    {
                        writer.WriteValue(attributeString);
                    }
                    else if (attributeValue is bool attributeBool)
                    {
                        writer.WriteValue(attributeBool);
                    }
                    else if (attributeValue is int attributeInt)
                    {
                        writer.WriteValue(attributeValue);
                    }
                    else
                    {
                        serializer.Serialize(writer, attributeValue);
                    }
                }
            }
            if (startedAttributeSection)
            {
                writer.WriteEndObject();
            }

            //serialize relationships
            var startedRelationshipSection = false;

            //first go through our relationships that were originally declared as attributes
            for (var i = 0; undeclaredRelationships != null && i < undeclaredRelationships.Count; i++)
            {
                var relationshipProperty = undeclaredRelationships[i].Key;
                var relationshipValue    = undeclaredRelationships[i].Value;
                if (!startedRelationshipSection)
                {
                    startedRelationshipSection = true;
                    writer.WritePropertyName(PropertyNames.Relationships);
                    writer.WriteStartObject();
                }

                if (relationshipProperty.MemberConverter != null)
                {
                    serializationData.ConverterStack.Push(relationshipProperty.MemberConverter);
                }

                writer.WritePropertyName(relationshipProperty.PropertyName);
                jsonApiContractResolver.ResourceRelationshipConverter.WriteNullableJson(
                    writer,
                    relationshipProperty.PropertyType,
                    relationshipValue,
                    serializer);

                if (relationshipProperty.MemberConverter != null)
                {
                    serializationData.ConverterStack.Pop();
                }
            }

            //then go through the ones we know to be relationships
            for (var i = 0; i < metadata.Relationships.Length; i++)
            {
                var relationshipProperty = metadata.Relationships[i];
                if (WriterUtil.ShouldWriteProperty(value, relationshipProperty, serializer, out object relationshipValue))
                {
                    if (!startedRelationshipSection)
                    {
                        startedRelationshipSection = true;
                        writer.WritePropertyName(PropertyNames.Relationships);
                        writer.WriteStartObject();
                    }

                    if (relationshipProperty.MemberConverter != null)
                    {
                        serializationData.ConverterStack.Push(relationshipProperty.MemberConverter);
                    }

                    writer.WritePropertyName(relationshipProperty.PropertyName);
                    jsonApiContractResolver.ResourceRelationshipConverter.WriteNullableJson(
                        writer,
                        relationshipProperty.PropertyType,
                        relationshipValue,
                        serializer);

                    if (relationshipProperty.MemberConverter != null)
                    {
                        serializationData.ConverterStack.Pop();
                    }
                }
            }
            if (startedRelationshipSection)
            {
                writer.WriteEndObject();
            }

            writer.WriteEndObject();

            //add reference to this type, so others can reference it
            if (id != null)
            {
                var reference = new ResourceObjectReference(id, type);
                serializationData.RenderedIncluded.Add(reference);
            }

            serializationData.ConverterStack.Pop();
        }
        protected void WriteReferenceObjectJson(JsonWriter writer, object value, JsonSerializer serializer, JsonObjectContract contract = null)
        {
            contract = contract ?? (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());

            writer.WriteStartObject();

            //A "resource identifier object" MUST contain type and id members.
            writer.WritePropertyName(PropertyNames.Id);
            var idProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Id);
            var idVal  = idProp?.ValueProvider?.GetValue(value) ?? string.Empty;

            serializer.Serialize(writer, idVal);

            writer.WritePropertyName(PropertyNames.Type);
            var typeProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Type);
            var typeVal  = typeProp?.ValueProvider?.GetValue(value) ?? GenerateDefaultTypeName(value.GetType());

            serializer.Serialize(writer, typeVal);

            //we will only write the object to included if there are properties that have have data
            //that we cant include within the reference
            var willWriteObjectToIncluded = contract.Properties.Any(prop =>
            {
                //ignore id, type, meta and ignored properties
                if (prop.PropertyName == PropertyNames.Id ||
                    prop.PropertyName == PropertyNames.Type ||
                    prop.PropertyName == PropertyNames.Meta ||
                    prop.Ignored)
                {
                    return(false);
                }

                //ignore null properties
                var propValue = prop.ValueProvider.GetValue(value);
                if (propValue == null)
                {
                    if (prop.NullValueHandling != null)
                    {
                        if (prop.NullValueHandling == NullValueHandling.Ignore)
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        if (serializer.NullValueHandling == NullValueHandling.Ignore)
                        {
                            return(false);
                        }
                    }
                }
                //we have another property with a value
                return(true);
            });

            //typeically we would just write the meta in the included. But if we are not going to
            //have something in included we will write the meta inline here
            if (!willWriteObjectToIncluded)
            {
                var metaProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Meta);
                var metaVal  = metaProp?.ValueProvider?.GetValue(value);
                if (metaVal != null)
                {
                    writer.WritePropertyName(PropertyNames.Meta);
                    serializer.Serialize(writer, metaVal);
                }
            }


            writer.WriteEndObject();


            if (willWriteObjectToIncluded)
            {
                var serializationData = SerializationData.GetSerializationData(writer);
                var reference         = new ResourceObjectReference(idVal.ToString(), typeVal.ToString());
                serializationData.Included[reference] = value;
            }
        }
        protected void WriteFullObjectJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var valueType = value.GetType();
            var contract  = (JsonObjectContract)serializer.ContractResolver.ResolveContract(valueType);

            writer.WriteStartObject();

            //will capture id and type as we go through
            object id   = null;
            object type = null;

            //A resource object MUST contain at least the following top-level members: type
            var typeProp = contract.Properties.GetClosestMatchProperty("type");

            if (typeProp == null)
            {
                writer.WritePropertyName("type");
                type = GenerateDefaultTypeName(valueType);
                serializer.Serialize(writer, GenerateDefaultTypeName(valueType));
            }

            List <JsonWriterCapture> attributes    = new List <JsonWriterCapture>();
            List <JsonWriterCapture> relationships = new List <JsonWriterCapture>();

            foreach (var prop in contract.Properties.Where(x => !x.Ignored))
            {
                var propValue = prop.ValueProvider.GetValue(value);
                if (propValue == null && (prop.NullValueHandling ?? serializer.NullValueHandling) == NullValueHandling.Ignore)
                {
                    continue;
                }

                switch (prop.PropertyName)
                {
                //In addition, a resource object MAY contain any of these top - level members: links, meta, attributes, relationships
                case PropertyNames.Id:     //Id is optional on base objects
                    id = propValue;
                    writer.WritePropertyName(prop.PropertyName);
                    serializer.Serialize(writer, id);
                    break;

                case PropertyNames.Links:
                case PropertyNames.Meta:
                    writer.WritePropertyName(prop.PropertyName);
                    serializer.Serialize(writer, propValue);
                    break;

                case PropertyNames.Type:
                    writer.WritePropertyName("type");
                    type = typeProp?.ValueProvider?.GetValue(value) ?? GenerateDefaultTypeName(valueType);
                    serializer.Serialize(writer, type);
                    break;

                default:
                    //we do not know if it is an Attribute or a Relationship
                    //so we will send out a probe to determine which one it is
                    var probe = new AttributeOrRelationshipProbe(writer);
                    probe.WritePropertyName(prop.PropertyName);
                    WriterUtil.WritePropertyValue(serializer, prop, propValue, probe);

                    (probe.PropertyType == AttributeOrRelationshipProbe.Type.Attribute
                            ? attributes
                            : relationships).Add(probe);
                    break;
                }
            }

            //add reference to this type, so others can reference it
            var serializationData = SerializationData.GetSerializationData(writer);
            var reference         = new ResourceObjectReference(id?.ToString(), type?.ToString());

            serializationData.Included[reference] = value;
            serializationData.RenderedIncluded.Add(reference);

            //output our attibutes in an attribute tag
            if (attributes.Count > 0)
            {
                writer.WritePropertyName(PropertyNames.Attributes);
                writer.WriteStartObject();
                foreach (var attribute in attributes)
                {
                    attribute.ApplyCaptured();
                }
                writer.WriteEndObject();
            }

            //output our relationships in a relationship tag
            if (relationships.Count > 0)
            {
                writer.WritePropertyName(PropertyNames.Relationships);
                writer.WriteStartObject();
                foreach (var relationship in relationships)
                {
                    relationship.ApplyCaptured();
                }
                writer.WriteEndObject();
            }

            writer.WriteEndObject();
        }
        protected void WriteReferenceObjectJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var contractResolver = serializer.ContractResolver;

            if (!(contractResolver.ResolveContract(value.GetType()) is JsonObjectContract contract &&
                  contract.Converter is ResourceObjectConverter))
            {
                throw new JsonApiFormatException(writer.Path,
                                                 $"Expected to find to find resource object, but found '{value}'",
                                                 "Resource indentifier objects MUST contain 'id' members");
            }

            writer.WriteStartObject();

            //A "resource identifier object" MUST contain type and id members.
            writer.WritePropertyName(PropertyNames.Id);
            var idProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Id);
            var idVal  = idProp?.ValueProvider?.GetValue(value) ?? string.Empty;

            serializer.Serialize(writer, idVal);

            writer.WritePropertyName(PropertyNames.Type);
            var typeProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Type);
            var typeVal  = typeProp?.ValueProvider?.GetValue(value) ?? GenerateDefaultTypeName(value.GetType());

            serializer.Serialize(writer, typeVal);

            //we will only write the object to included if there are properties that have have data
            //that we cant include within the reference
            var willWriteObjectToIncluded = contract.Properties.Any(prop =>
            {
                //ignore id, type, meta and ignored properties
                if (prop.PropertyName == PropertyNames.Id ||
                    prop.PropertyName == PropertyNames.Type ||
                    prop.PropertyName == PropertyNames.Meta ||
                    prop.Ignored)
                {
                    return(false);
                }

                //ignore null properties
                var propValue = prop.ValueProvider.GetValue(value);
                return(propValue != null || (prop.NullValueHandling ?? serializer.NullValueHandling) == NullValueHandling.Include);
            });

            //typeically we would just write the meta in the included. But if we are not going to
            //have something in included we will write the meta inline here
            if (!willWriteObjectToIncluded)
            {
                var metaProp = contract.Properties.GetClosestMatchProperty(PropertyNames.Meta);
                var metaVal  = metaProp?.ValueProvider?.GetValue(value);
                if (metaVal != null)
                {
                    writer.WritePropertyName(PropertyNames.Meta);
                    serializer.Serialize(writer, metaVal);
                }
            }

            writer.WriteEndObject();

            if (willWriteObjectToIncluded)
            {
                var serializationData = SerializationData.GetSerializationData(writer);
                var reference         = new ResourceObjectReference(idVal.ToString(), typeVal.ToString());
                serializationData.Included[reference] = value;
            }
        }
        protected void WriteFullObjectJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var valueType        = value.GetType();
            var contractResolver = serializer.ContractResolver;

            if (!(contractResolver.ResolveContract(valueType) is JsonObjectContract contract &&
                  contract.Converter is ResourceObjectConverter))
            {
                throw new JsonApiFormatException(writer.Path,
                                                 $"Expected to find to find resource object, but found '{value}'",
                                                 "Resource indentifier objects MUST contain 'id' members");
            }

            //prepare to start capturing attributes and relationships
            var attributes    = new List <KeyValuePair <JsonProperty, object> >();
            var relationships = new List <KeyValuePair <JsonProperty, object> >();

            //will capture id and type as we go through
            object id   = null;
            object type = null;

            writer.WriteStartObject();

            //A resource object MUST contain at least the following top-level members: type
            var typeProp = contract.Properties.GetClosestMatchProperty("type");

            if (typeProp == null)
            {
                writer.WritePropertyName("type");
                type = GenerateDefaultTypeName(valueType);
                serializer.Serialize(writer, type);
            }

            foreach (var prop in contract.Properties.Where(x => !x.Ignored))
            {
                var propValue = prop.ValueProvider.GetValue(value);
                if (propValue == null && (prop.NullValueHandling ?? serializer.NullValueHandling) == NullValueHandling.Ignore)
                {
                    continue;
                }
                var propType = propValue?.GetType() ?? prop.PropertyType;

                switch (prop.PropertyName)
                {
                //In addition, a resource object MAY contain any of these top - level members: links, meta, attributes, relationships
                case PropertyNames.Id:     //Id is optional on base objects
                    id = propValue;
                    writer.WritePropertyName(PropertyNames.Id);
                    serializer.Serialize(writer, id);
                    break;

                case PropertyNames.Links:
                case PropertyNames.Meta:
                    writer.WritePropertyName(prop.PropertyName);
                    serializer.Serialize(writer, propValue);
                    break;

                case PropertyNames.Type:
                    writer.WritePropertyName(PropertyNames.Type);
                    type = typeProp?.ValueProvider?.GetValue(value) ?? GenerateDefaultTypeName(valueType);
                    serializer.Serialize(writer, type);
                    break;

                case var _ when TryParseAsRelationship(contractResolver.ResolveContract(propType), propValue, out var relationshipObj):
                    relationships.Add(new KeyValuePair <JsonProperty, object>(prop, relationshipObj));

                    break;

                default:
                    attributes.Add(new KeyValuePair <JsonProperty, object>(prop, propValue));
                    break;
                }
            }

            //output our attibutes
            if (attributes.Count > 0)
            {
                writer.WritePropertyName(PropertyNames.Attributes);
                writer.WriteStartObject();
                foreach (var attribute in attributes)
                {
                    writer.WritePropertyName(attribute.Key.PropertyName);
                    WriterUtil.SerializeValueWithMemberConvertor(serializer, writer, attribute.Key, attribute.Value);
                }
                writer.WriteEndObject();
            }

            //output our relationships
            if (relationships.Count > 0)
            {
                writer.WritePropertyName(PropertyNames.Relationships);
                writer.WriteStartObject();
                foreach (var relationship in relationships)
                {
                    writer.WritePropertyName(relationship.Key.PropertyName);
                    serializer.Serialize(writer, relationship.Value);
                }
                writer.WriteEndObject();
            }

            writer.WriteEndObject();

            //add reference to this type, so others can reference it
            var serializationData = SerializationData.GetSerializationData(writer);
            var reference         = new ResourceObjectReference(id?.ToString(), type?.ToString());

            serializationData.Included[reference] = value;
            serializationData.RenderedIncluded.Add(reference);
        }