Example #1
0
        private object ReadJsonAsResourceObject(ForkableJsonReader reader, Type objectType, JsonSerializer serializer)
        {
            // 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);
            }

            var serializationData       = SerializationData.GetSerializationData(reader);
            var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver;

            var reference = ReaderUtil.ReadAheadToIdentifyObject(reader);

            if (serializationData.Included.TryGetValue(reference, out object resourceObject))
            {
                if (resourceObject is JObject resoruceObjectJObject)
                {
                    // 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 now know what type it should be so will read it
                    // as such
                    var resourceObjectReader = new ForkableJsonReader(resoruceObjectJObject.CreateReader(), reader.SerializationDataToken);
                    resourceObjectReader.Read(); //JObject readers begin at Not Started
                    resourceObject = jsonApiContractResolver.ResourceObjectConverter.ReadJson(
                        resourceObjectReader,
                        objectType,
                        null,
                        serializer);
                }

                //push the reader to the end, we dont need anything else out of the reference
                ReaderUtil.ReadUntilEnd(reader, reader.Path);
            }
            else
            {
                var contract = (JsonObjectContract)jsonApiContractResolver.ResolveContract(objectType);

                resourceObject = ReaderUtil.CreateObject(serializationData, objectType, reference.Type, serializer);

                // for placeholders we will just read the top level properties
                // it is unlikely to have attributes/relationships present
                foreach (var propName in ReaderUtil.IterateProperties(reader))
                {
                    var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty(
                        serializer,
                        resourceObject,
                        contract.Properties.GetClosestMatchProperty(propName),
                        reader);
                }

                serializationData.Included[reference] = resourceObject;
            }

            if (!TypeInfoShim.IsInstanceOf(objectType.GetTypeInfo(), resourceObject))
            {
                throw new JsonSerializationException($"Unable to assign object '{resourceObject}' to type '{objectType}' at path {reader.FullPath}");
            }

            return(resourceObject);
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var serializationData = SerializationData.GetSerializationData(reader);

            reader = new ForkableJsonReader(reader);

            var contract   = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
            var rootObject = contract.DefaultCreator();

            serializationData.HasProcessedDocumentRoot = true;

            var includedConverter = new IncludedConverter();

            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                switch (propName)
                {
                case PropertyNames.Data:

                    var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo())
                                                    .Select(x => x.GetTypeInfo())
                                                    .FirstOrDefault(x =>
                                                                    x.IsGenericType &&
                                                                    x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>));
                    var dataType = documentRootInterfaceType.GenericTypeArguments[0];

                    var dataObj = serializer.Deserialize(reader, dataType);
                    contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj);
                    break;

                case PropertyNames.Included:

                    //if our object has an included property we will do our best to populate it
                    var property = contract.Properties.GetClosestMatchProperty(propName);
                    if (ReaderUtil.CanPopulateProperty(property))
                    {
                        ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), ((ForkableJsonReader)reader).Fork());
                    }

                    //still need to read our values so they are updated
                    foreach (var obj in ReaderUtil.IterateList(reader))
                    {
                        var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer);
                    }

                    break;

                default:
                    ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader);
                    break;
                }
            }
            return(rootObject);
        }
Example #3
0
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var jsonApiContractResolver = (JsonApiContractResolver)serializer.ContractResolver;
            var contract = jsonApiContractResolver.ResolveContract(objectType);

            // we be a ResourceObject rather than a RelationshpObject
            // if so we will just read the data property of the resource object
            if (!(contract is ResourceRelationshipContract rrc))
            {
                return(ReadJsonDataPropertyAsResourceObject(reader, objectType, existingValue, serializer, jsonApiContractResolver));
            }


            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);
            }

            //create a new relationship object and start populating the properties
            var obj = rrc.DefaultCreator();

            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                switch (propName)
                {
                case PropertyNames.Data:
                    ReaderUtil.TryPopulateProperty(
                        serializer,
                        obj,
                        rrc.Properties.GetClosestMatchProperty(propName),
                        reader,
                        overrideConverter: jsonApiContractResolver.ResourceIdentifierConverter);
                    break;

                default:
                    ReaderUtil.TryPopulateProperty(
                        serializer,
                        obj,
                        rrc.Properties.GetClosestMatchProperty(propName),
                        reader);
                    break;
                }
            }
            return(obj);
        }
Example #4
0
        private object ReadJsonAsExplicitResourceIdentifier(ForkableJsonReader reader, Type objectType, JsonSerializer serializer)
        {
            if (ReaderUtil.TryUseCustomConvertor(reader, objectType, null, 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);
            }

            var serializationData          = SerializationData.GetSerializationData(reader);
            var jsonApiContractResolver    = (JsonApiContractResolver)serializer.ContractResolver;
            var resourceIdentifierContract = (ResourceIdentifierContract)jsonApiContractResolver.ResolveContract(objectType);
            var resourceIdentifier         = resourceIdentifierContract.DefaultCreator();

            var reference = ReaderUtil.ReadAheadToIdentifyObject(reader);

            var explicitResourceIdentifierReader = reader.Fork();

            foreach (var innerPropName in ReaderUtil.IterateProperties(explicitResourceIdentifierReader))
            {
                ReaderUtil.TryPopulateProperty(
                    serializer,
                    resourceIdentifier,
                    resourceIdentifierContract.Properties.GetClosestMatchProperty(innerPropName),
                    explicitResourceIdentifierReader);
            }

            var resourceObject = ReadJsonAsResourceObject(
                reader,
                resourceIdentifierContract.ResourceObjectProperty.PropertyType,
                serializer);

            //we will only set the resource object if we have rendered the included
            //value somehwere, if we have not it means the value was actaully provided
            var valueProvider = resourceIdentifierContract.ResourceObjectProperty.ValueProvider;

            serializationData.PostProcessingActions.Add(() =>
            {
                if (serializationData.RenderedIncluded.Contains(reference))
                {
                    valueProvider.SetValue(resourceIdentifier, resourceObject);
                }
            });

            return(resourceIdentifier);
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            reader = new ForkableJsonReader(reader);

            var contract   = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
            var rootObject = contract.DefaultCreator();

            serializer.ReferenceResolver.AddReference(null, IncludedReferenceResolver.RootReference, rootObject);

            var includedConverter = new IncludedConverter();

            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                switch (propName)
                {
                case PropertyNames.Data:

                    var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo())
                                                    .Select(x => x.GetTypeInfo())
                                                    .FirstOrDefault(x =>
                                                                    x.IsGenericType &&
                                                                    x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>));
                    var dataType = documentRootInterfaceType.GenericTypeArguments[0];

                    var dataObj = serializer.Deserialize(reader, dataType);
                    contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj);
                    break;

                case PropertyNames.Included:
                    foreach (var obj in ReaderUtil.IterateList(reader))
                    {
                        var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer);
                    }
                    break;

                default:
                    ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader);
                    break;
                }
            }
            return(rootObject);
        }
        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);
        }
        protected void PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader)
        {
            JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(obj.GetType());

            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty(
                    serializer,
                    obj,
                    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,
                            obj,
                            contract.Properties.GetClosestMatchProperty(innerPropName),
                            reader);
                    }
                }

                //flatten out relationships onto the object
                if (!successfullyPopulateProperty && propName == PropertyNames.Relationships)
                {
                    foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                    {
                        ReaderUtil.TryPopulateProperty(
                            serializer,
                            obj,
                            contract.Properties.GetClosestMatchProperty(innerPropName),
                            reader);
                    }
                }
            }
        }
        protected void PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader, JsonObjectContract contract)
        {
            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty(
                    serializer,
                    obj,
                    contract.Properties.GetClosestMatchProperty(propName),
                    reader);

                //flatten out attributes onto the object
                if (!successfullyPopulateProperty && propName == "attributes")
                {
                    foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                    {
                        ReaderUtil.TryPopulateProperty(
                            serializer,
                            obj,
                            contract.Properties.GetClosestMatchProperty(innerPropName),
                            reader);
                    }
                }

                //flatten out relationships onto the object
                if (!successfullyPopulateProperty && propName == "relationships")
                {
                    foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                    {
                        ReaderUtil.TryPopulateProperty(
                            serializer,
                            obj,
                            contract.Properties.GetClosestMatchProperty(innerPropName),
                            reader);
                    }
                }
            }
        }
Example #9
0
        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 object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var contract   = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
            var rootObject = contract.DefaultCreator();

            serializer.ReferenceResolver.AddReference(null, IncludedReferenceResolver.RootReference, rootObject);
            //var includedConverter = new IncludedConverter();

            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                switch (propName)
                {
                case PropertyNames.Data:

                    var documentRootInterfaceType = TypeInfoShim.GetInterfaces(objectType.GetTypeInfo())
                                                    .Select(x => x.GetTypeInfo())
                                                    .FirstOrDefault(x =>
                                                                    x.IsGenericType &&
                                                                    x.GetGenericTypeDefinition() == typeof(IDocumentRoot <>));
                    var dataType = documentRootInterfaceType.GenericTypeArguments[0];

                    var dataObj = serializer.Deserialize(reader, dataType);
                    contract.Properties.GetClosestMatchProperty(PropertyNames.Data).ValueProvider.SetValue(rootObject, dataObj);
                    break;

                case PropertyNames.Included:

                    //    //if our object has an included property we will do our best to populate it
                    //    var property = contract.Properties.GetClosestMatchProperty(propName);
                    //    //if (ReaderUtil.CanPopulateProperty(property))
                    //    //{
                    //    //    ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), ((ForkableJsonReader)reader).Fork());
                    //    //}

                    //still need to read our values so they are updated
                    foreach (var obj in ReaderUtil.IterateList(reader))
                    {
                        var type = "";
                        var id   = "";
                        //read untill id and type
                        foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                        {
                            switch (innerPropName)
                            {
                            case PropertyNames.Type:
                                type = reader.Value.ToString();
                                break;

                            case PropertyNames.Id:
                                id = reader.Value.ToString();
                                break;

                            default:
                                break;
                            }
                            if (!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(id))
                            {
                                break;
                            }
                        }
                        var existingObject = serializer.ReferenceResolver.ResolveReference(null, type + ":" + id);
                        if (existingObject != null)
                        {
                            //We have an existing object, its likely our included data has more detail than what
                            //is currently on the object so we will pass the reader so it can be deserialized again
                            var inc_type = existingObject.GetType();
                            var existingObjectContract = serializer.ContractResolver.ResolveContract(inc_type);
                            existingObjectContract.Converter.ReadJson(reader, inc_type, existingObject, serializer);
                        }
                        //contract.Converter.ReadJson(reader, typeof(object), rootObject, serializer);
                        //var includedObject = includedConverter.ReadJson(reader, typeof(object), null, serializer);
                    }

                    break;

                default:
                    ReaderUtil.TryPopulateProperty(serializer, rootObject, contract.Properties.GetClosestMatchProperty(propName), reader);
                    break;
                }
            }
            return(rootObject);
        }
Example #11
0
        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);
        }
        protected object PopulateProperties(JsonSerializer serializer, object obj, JsonReader reader, JsonObjectContract contract)
        {
            foreach (var propName in ReaderUtil.IterateProperties(reader))
            {
                if (propName == PropertyNames.Type)
                {
                    var type = reader.Value;
                    if (obj.GetType().Name.ToLower() != type.ToString().ToLower())
                    {
                        Assembly[] assembly = Utility.GetAssemblies();
                        var        tp       = assembly.SelectMany(s => s.GetTypes()).ToList().Where(t => t.Name.ToLower() == type.ToString().ToLower()).SingleOrDefault();
                        if (tp != null)
                        {
                            obj      = Activator.CreateInstance(tp);
                            contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(obj.GetType());
                        }
                    }
                    //retObj.id = obj;
                }
                var successfullyPopulateProperty = ReaderUtil.TryPopulateProperty(
                    serializer,
                    obj,
                    contract.Properties.GetClosestMatchProperty(propName),
                    reader);

                if (!successfullyPopulateProperty)
                {
                    //flatten out attributes onto the object
                    if (propName == "attributes")
                    {
                        foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                        {
                            ReaderUtil.TryPopulateProperty(
                                serializer,
                                obj,
                                contract.Properties.GetClosestMatchProperty(innerPropName),
                                reader);
                        }
                    }

                    //flatten out relationships onto the object
                    if (propName == "relationships")
                    {
                        foreach (var innerPropName in ReaderUtil.IterateProperties(reader))
                        {
                            //read into the 'Data' path
                            var preDataPath = ReaderUtil.ReadUntilStart(reader, DataPathRegex);

                            ReaderUtil.TryPopulateProperty(
                                serializer,
                                obj,
                                contract.Properties.GetClosestMatchProperty(innerPropName),
                                reader);
                            //read out of the 'Data' path
                            ReaderUtil.ReadUntilEnd(reader, preDataPath);
                        }
                    }
                }
            }
            return(obj);
        }