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; } var contractResolver = serializer.ContractResolver; var enumerable = value as IEnumerable <object> ?? Enumerable.Empty <object>(); writer.WriteStartArray(); foreach (var valueElement in enumerable) { if (valueElement == null || !(contractResolver.ResolveContract(valueElement.GetType()) is ResourceObjectContract)) { throw new JsonApiFormatException(writer.Path, $"Expected to find to find resource objects within lists, but found '{valueElement}'", "Resource identifier objects MUST contain 'id' members"); } serializer.Serialize(writer, valueElement); } writer.WriteEndArray(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object list; if (DocumentRootConverter.TryResolveAsRootData(reader, objectType, serializer, out list)) { return(list); } //read into the 'Data' path var preDataPath = ReaderUtil.ReadUntilStart(reader, DataPathRegex); //we should be dealing with list types, but we also want the element type Type elementType; if (!ListUtil.IsList(objectType, out elementType)) { throw new ArgumentException($"{typeof(ResourceObjectListConverter)} can only read json lists", nameof(objectType)); } var itemsIterator = ReaderUtil.IterateList(reader).Select(x => serializer.Deserialize(reader, elementType)); list = ListUtil.CreateList(objectType, itemsIterator); //read out of the 'Data' path ReaderUtil.ReadUntilEnd(reader, preDataPath); return(list); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (DocumentRootConverter.TryResolveAsRootData(writer, value, serializer)) { return; } //if they have custom convertors registered, we will respect them var customConvertor = serializer.Converters.FirstOrDefault(x => x.CanWrite && x.CanConvert(value.GetType())); if (customConvertor != null && customConvertor != this) { customConvertor.WriteJson(writer, value, serializer); return; } var isRelationship = RelationshipPathRegex.IsMatch(writer.Path); if (isRelationship) { WriteReferenceObjectJson(writer, value, serializer); } else { WriteFullObjectJson(writer, value, serializer); } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var serializationData = SerializationData.GetSerializationData(writer); if (!serializationData.HasProcessedDocumentRoot) { DocumentRootConverter.ResolveAsRootError(writer, value, serializer); return; } var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType()); writer.WriteStartObject(); 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; } writer.WritePropertyName(prop.PropertyName); serializer.Serialize(writer, propValue); } writer.WriteEndObject(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root object obj; if (DocumentRootConverter.TryResolveAsRootData(reader, objectType, serializer, out obj)) { return(obj); } //read into the 'Data' element return(ReaderUtil.ReadInto( reader as ForkableJsonReader ?? new ForkableJsonReader(reader), DataReadPathRegex, dataReader => { //if the value has been explicitly set to null then the value of the element is simply null if (dataReader.TokenType == JsonToken.Null) { return null; } JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var serializationData = SerializationData.GetSerializationData(dataReader); //if we arent given an existing value check the references to see if we have one in there //if we dont have one there then create a new object to populate if (existingValue == null) { var reference = ReaderUtil.ReadAheadToIdentifyObject(dataReader); if (!serializationData.Included.TryGetValue(reference, out existingValue)) { existingValue = contract.DefaultCreator(); serializationData.Included.Add(reference, existingValue); } if (existingValue is JObject) { //sometimes the value in the reference resolver is a JObject. This occurs when we //did not know what type it should be when we first read it (i.e. included was processed //before the item). In these cases we will create a new object and read data from the JObject dataReader = new ForkableJsonReader(((JObject)existingValue).CreateReader(), dataReader.SerializationDataToken); dataReader.Read(); //JObject readers begin at Not Started existingValue = contract.DefaultCreator(); serializationData.Included[reference] = existingValue; } } PopulateProperties(serializer, existingValue, dataReader, contract); return existingValue; })); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (DocumentRootConverter.TryResolveAsRootError(writer, value, serializer)) { return; } var enumerable = value as IEnumerable <object> ?? Enumerable.Empty <object>(); writer.WriteStartArray(); foreach (var valueElement in enumerable) { serializer.Serialize(writer, valueElement); } writer.WriteEndArray(); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (DocumentRootConverter.TryResolveAsRootData(writer, value, serializer)) { return; } var isRelationship = RelationshipPathRegex.IsMatch(writer.Path); if (isRelationship) { WriteReferenceObjectJson(writer, value, serializer); } else { WriteFullObjectJson(writer, value, serializer); } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (DocumentRootConverter.TryResolveAsRootData(writer, value, serializer)) { return; } WriterUtil.WriteIntoElement(writer, DataPathRegex, PropertyNames.Data, () => { var enumerable = value as IEnumerable <object> ?? Enumerable.Empty <object>(); writer.WriteStartArray(); foreach (var valueElement in enumerable) { serializer.Serialize(writer, valueElement); } writer.WriteEndArray(); }); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root IEnumerable <IError> errors; if (DocumentRootConverter.TryResolveAsRootError(reader, objectType, serializer, out errors)) { return(ListUtil.CreateList(objectType, errors)); } Type elementType; ListUtil.IsList(objectType, out elementType); return(ListUtil.CreateList(objectType, ReaderUtil.IterateList(reader) .Select(x => serializer.Deserialize(reader, elementType)))); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var serializationData = SerializationData.GetSerializationData(writer); if (!serializationData.HasProcessedDocumentRoot) { DocumentRootConverter.ResolveAsRootError(writer, value, serializer); return; } var enumerable = value as IEnumerable <object> ?? Enumerable.Empty <object>(); writer.WriteStartArray(); foreach (var valueElement in enumerable) { serializer.Serialize(writer, valueElement); } writer.WriteEndArray(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { var errors = DocumentRootConverter.ResolveAsRootError(reader, objectType, serializer); return(ListUtil.CreateList(objectType, errors)); } Type elementType; ListUtil.IsList(objectType, out elementType); return(ListUtil.CreateList(objectType, ReaderUtil.IterateList(reader) .Select(x => serializer.Deserialize(reader, elementType)))); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { return(DocumentRootConverter.ResolveAsRootData(reader, objectType, serializer)); } //we should be dealing with list types, but we also want the element type if (!ListUtil.IsList(objectType, out Type elementType)) { throw new ArgumentException($"{typeof(ResourceObjectListConverter)} can only read json lists", nameof(objectType)); } var itemsIterator = ReaderUtil.IterateList(reader).Select(x => serializer.Deserialize(reader, elementType)); var list = ListUtil.CreateList(objectType, itemsIterator); return(list); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root IEnumerable <IError> errors; if (DocumentRootConverter.TryResolveAsRootError(reader, objectType, serializer, out errors)) { //not sure if this is the correct thing to do. We are deserializing a single //error but json:api always gives us a list of errors. We just return the first //error return(errors.First()); } var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var error = (IError)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator(); foreach (var propName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty(serializer, error, contract.Properties.GetClosestMatchProperty(propName), reader); } return(error); }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (DocumentRootConverter.TryResolveAsRootData(writer, value, serializer)) { return; } WriterUtil.WriteIntoElement(writer, DataWritePathRegex, PropertyNames.Data, () => { var probe = writer as AttributeOrRelationshipProbe; if (probe != null) { //if someone is sending a probe its because we are in a relationship property. //let the probe know we are in a relationship and write the reference element probe.PropertyType = AttributeOrRelationshipProbe.Type.Relationship; WriteReferenceObjectJson(writer, value, serializer); } else { WriteFullObjectJson(writer, value, serializer); } }); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { return(DocumentRootConverter.ResolveAsRootData(reader, objectType, serializer)); } if (ReaderUtil.TryUseCustomConvertor(reader, objectType, existingValue, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } //if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } serializationData.ConverterStack.Push(this); var forkableReader = reader as ForkableJsonReader ?? new ForkableJsonReader(reader); reader = forkableReader; var contractResolver = (JsonApiContractResolver)serializer.ContractResolver; var reference = ReaderUtil.ReadAheadToIdentifyObject(forkableReader); //if we dont have this object already we will create it existingValue = existingValue ?? CreateObject(objectType, reference.Type, serializer); JsonContract rawContract = contractResolver.ResolveContract(existingValue.GetType()); if (!(rawContract is JsonObjectContract contract)) { throw new JsonApiFormatException( forkableReader.FullPath, $"Expected created object to be a resource object, but found '{existingValue}'", "Resource indentifier objects MUST contain 'id' members"); } foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Attributes) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Relationships) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { var prop = contract.Properties.GetClosestMatchProperty(innerPropName); if (prop == null) { continue; } // This is a massive hack. MemberConverters used to work and allowed // modifying a members create object. Unfortunatly Resource Identifiers // are no longer created by this object converter. We are passing the // convertor via the serialization data so ResourceIdentifierConverter // can access it down the line. // next breaking change remove support for ResourceObjectConverter // member converters if (prop.MemberConverter != null) { serializationData.ConverterStack.Push(prop.MemberConverter); } ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader, overrideConverter: contractResolver.ResourceRelationshipConverter); if (prop.MemberConverter != null) { serializationData.ConverterStack.Pop(); } } } } //we have rendered this so we will tag it as included serializationData.RenderedIncluded.Add(reference); serializationData.Included[reference] = existingValue; serializationData.ConverterStack.Pop(); return(existingValue); }
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(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root if (DocumentRootConverter.TryResolveAsRootData(reader, objectType, serializer, out object obj)) { return(obj); } //read into the 'Data' element return(ReaderUtil.ReadInto( reader as ForkableJsonReader ?? new ForkableJsonReader(reader), DataReadPathRegex, dataReader => { //if they have custom convertors registered, we will respect them var customConvertor = serializer.Converters.FirstOrDefault(x => x.CanRead && x.CanConvert(objectType)); if (customConvertor != null && customConvertor != this) { return customConvertor.ReadJson(reader, objectType, existingValue, serializer); } //if the value has been explicitly set to null then the value of the element is simply null if (dataReader.TokenType == JsonToken.Null) { return null; } var serializationData = SerializationData.GetSerializationData(dataReader); //if we arent given an existing value check the references to see if we have one in there //if we dont have one there then create a new object to populate if (existingValue == null) { var reference = ReaderUtil.ReadAheadToIdentifyObject(dataReader); if (!serializationData.Included.TryGetValue(reference, out existingValue)) { existingValue = CreateObject(objectType, reference.Type, serializer); serializationData.Included.Add(reference, existingValue); } if (existingValue is JObject existingValueJObject) { //sometimes the value in the reference resolver is a JObject. This occurs when we //did not know what type it should be when we first read it (i.e. included was processed //before the item). In these cases we will create a new object and read data from the JObject dataReader = new ForkableJsonReader(existingValueJObject.CreateReader(), dataReader.SerializationDataToken); dataReader.Read(); //JObject readers begin at Not Started existingValue = CreateObject(objectType, reference.Type, serializer); serializationData.Included[reference] = existingValue; } } //additional check to ensure the object we created is of the correct type if (!TypeInfoShim.IsInstanceOf(objectType.GetTypeInfo(), existingValue)) { throw new JsonSerializationException($"Unable to assign object '{existingValue}' to type '{objectType}'"); } PopulateProperties(serializer, existingValue, dataReader); return existingValue; })); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { //we may be starting the deserialization here, if thats the case we need to resolve this object as the root var serializationData = SerializationData.GetSerializationData(reader); if (!serializationData.HasProcessedDocumentRoot) { return(DocumentRootConverter.ResolveAsRootData(reader, objectType, serializer)); } if (ReaderUtil.TryUseCustomConvertor(reader, objectType, existingValue, serializer, this, out object customConvertedValue)) { return(customConvertedValue); } //if the value has been explicitly set to null then the value of the element is simply null if (reader.TokenType == JsonToken.Null) { return(null); } serializationData.ConverterStack.Push(this); var forkableReader = reader as ForkableJsonReader ?? new ForkableJsonReader(reader); reader = forkableReader; var contractResolver = (JsonApiContractResolver)serializer.ContractResolver; var reference = ReaderUtil.ReadAheadToIdentifyObject(forkableReader); //if we dont have this object already we will create it existingValue = existingValue ?? CreateObject(objectType, reference.Type, serializer); //mark this object as a possible include. We need to do this before deserialiazing //the relationship; It could have relationships that reference back to this object serializationData.Included[reference] = existingValue; JsonContract rawContract = contractResolver.ResolveContract(existingValue.GetType()); if (!(rawContract is JsonObjectContract contract)) { throw new JsonApiFormatException( forkableReader.FullPath, $"Expected created object to be a resource object, but found '{existingValue}'", "Resource indentifier objects MUST contain 'id' members"); } foreach (var propName in ReaderUtil.IterateProperties(reader)) { var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(propName), reader); //flatten out attributes onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Attributes) { // AH 2020-08-27 - store unknown Attributes in this dictionary. Dictionary <string, object> extraAttributes = new Dictionary <string, object>(); JsonProperty extraAttributesProperty = contract.Properties.GetClosestMatchProperty("extraAttributes"); foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { JsonProperty matchedProperty = contract.Properties.GetClosestMatchProperty(innerPropName); // regular behavior if (matchedProperty != null) { ReaderUtil.TryPopulateProperty( serializer, existingValue, matchedProperty, //contract.Properties.GetClosestMatchProperty(innerPropName), reader); } // modified behavior - store any unknown custom attributes into an extraAttributes collection else if (extraAttributesProperty != null) { object extraAttributeValue = null; // this is a Custom Attribute or an Attribute that is a list if (reader.TokenType == JsonToken.StartArray) { JArray jarr = JArray.Load(reader); if (jarr.Count != 0) { // if we have an object it's a custom attribute. if (jarr[0].Type == JTokenType.Object) { JObject jobj = jarr[0].ToObject <JObject>(); extraAttributeValue = jobj; } //otherwise the array itself is the value for the attribute (like entity_types.entity_attributes) else { extraAttributeValue = jarr; } } // don't read here. Next iteration expects us to be at the end of array/object //reader.Read(); } // It's a regular Attribute that we don't expect. Just store it's value. else { extraAttributeValue = reader.Value; } extraAttributes[innerPropName] = extraAttributeValue; } } if (extraAttributesProperty != null) { extraAttributesProperty.ValueProvider.SetValue(existingValue, extraAttributes); } } //flatten out relationships onto the object if (!successfullyPopulateProperty && propName == PropertyNames.Relationships) { foreach (var innerPropName in ReaderUtil.IterateProperties(reader)) { var prop = contract.Properties.GetClosestMatchProperty(innerPropName); if (prop == null) { continue; } // This is a massive hack. MemberConverters used to work and allowed // modifying a members create object. Unfortunatly Resource Identifiers // are no longer created by this object converter. We are passing the // convertor via the serialization data so ResourceIdentifierConverter // can access it down the line. // next breaking change remove support for ResourceObjectConverter // member converters if (prop.MemberConverter != null) { serializationData.ConverterStack.Push(prop.MemberConverter); } ReaderUtil.TryPopulateProperty( serializer, existingValue, contract.Properties.GetClosestMatchProperty(innerPropName), reader, overrideConverter: contractResolver.ResourceRelationshipConverter); if (prop.MemberConverter != null) { serializationData.ConverterStack.Pop(); } } } } //we have read the object so we will tag it as 'rendered' serializationData.RenderedIncluded.Add(reference); serializationData.ConverterStack.Pop(); return(existingValue); }