public object Deserialize(Type objectType, Stream readStream, JsonReader reader, JsonSerializer serializer)
        {
            object retval = Activator.CreateInstance(objectType);

            if (reader.TokenType != JsonToken.StartObject)
            {
                throw new JsonReaderException(String.Format("Expected JsonToken.StartObject, got {0}", reader.TokenType.ToString()));
            }
            reader.Read(); // Burn the StartObject token
            do
            {
                if (reader.TokenType == JsonToken.PropertyName)
                {
                    string       value = (string)reader.Value;
                    PropertyInfo prop  = _modelManager.GetPropertyForJsonKey(objectType, value);
                    // If the model object has a non-standard Id property, but the "id" key is being used...
                    if (prop == null && value == "id")
                    {
                        prop = _modelManager.GetIdProperty(objectType);
                    }

                    if (value == "links")
                    {
                        reader.Read(); // burn the PropertyName token
                        //TODO: linked resources (Done??)
                        DeserializeLinkedResources(retval, readStream, reader, serializer);
                    }
                    else if (prop != null)
                    {
                        if (!prop.PropertyType.CanWriteAsJsonApiAttribute())
                        {
                            reader.Read(); // burn the PropertyName token
                            //TODO: Embedded would be dropped here!
                            continue;      // These aren't supposed to be here, they're supposed to be in "links"!
                        }

                        object propVal;
                        Type   enumType;
                        if (prop.PropertyType == typeof(string) &&
                            prop.GetCustomAttributes().Any(attr => attr is SerializeStringAsRawJsonAttribute))
                        {
                            reader.Read();
                            if (reader.TokenType == JsonToken.Null)
                            {
                                propVal = null;
                            }
                            else
                            {
                                var token      = JToken.Load(reader);
                                var rawPropVal = token.ToString();
                                propVal = JsonHelpers.MinifyJson(rawPropVal);
                            }
                        }
                        else if (prop.PropertyType.IsGenericType &&
                                 prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>) &&
                                 (enumType = prop.PropertyType.GetGenericArguments()[0]).IsEnum)
                        {
                            // Nullable enums need special handling
                            reader.Read();
                            propVal = reader.TokenType == JsonToken.Null
                                ? null
                                : Enum.Parse(enumType, reader.Value.ToString());
                        }
                        else if (prop.PropertyType == typeof(DateTimeOffset) ||
                                 prop.PropertyType == typeof(DateTimeOffset?))
                        {
                            // For some reason
                            reader.ReadAsString();
                            propVal = reader.TokenType == JsonToken.Null
                                ? (object)null
                                : DateTimeOffset.Parse(reader.Value.ToString());
                        }
                        else
                        {
                            reader.Read();
                            propVal = DeserializeAttribute(prop.PropertyType, reader);
                        }


                        prop.SetValue(retval, propVal, null);

                        // Tell the MetadataManager that we deserialized this property
                        MetadataManager.Instance.SetMetaForProperty(retval, prop, true);

                        // pop the value off the reader, so we catch the EndObject token below!.
                        reader.Read();
                    }
                    else
                    {
                        // Unexpected/unknown property--Skip the propertyname and its value
                        reader.Skip();
                        if (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject)
                        {
                            reader.Skip();
                        }
                        else
                        {
                            reader.Read();
                        }
                    }
                }
            } while (reader.TokenType != JsonToken.EndObject);
            reader.Read(); // burn the EndObject token before returning back up the call stack

            /*
             * // Suss out all the relationship members, and which ones have what cardinality...
             * IEnumerable<PropertyInfo> relations = (
             *  from prop in objectType.GetProperties()
             *  where !CanWriteTypeAsJsonApiAttribute(prop.PropertyType)
             *  && prop.GetCustomAttributes(true).Any(attribute => attribute is System.Runtime.Serialization.DataMemberAttribute)
             *  select prop
             *  );
             * IEnumerable<PropertyInfo> hasManys = relations.Where(prop => typeof(IEnumerable<object>).IsAssignableFrom(prop.PropertyType));
             * IEnumerable<PropertyInfo> belongsTos = relations.Where(prop => !typeof(IEnumerable<object>).IsAssignableFrom(prop.PropertyType));
             *
             * JObject links = (JObject)jo["links"];
             *
             * // Lets deal with belongsTos first, that should be simpler...
             * foreach (PropertyInfo prop in belongsTos)
             * {
             *  if (links == null) break; // Well, apparently we don't have any data for the relationships!
             *
             *  string btId = (string)links[_modelManager.GetJsonKeyForProperty(prop)];
             *  if (btId == null)
             *  {
             *      prop.SetValue(retval, null, null); // Important that we set--the value may have been cleared!
             *      continue; // breaking early!
             *  }
             *  Type relType = prop.PropertyType;
             *  //if (typeof(EntityObject).IsAssignableFrom(relType))
             *  if (resolver.CanIncludeTypeAsObject(relType))
             *  {
             *      prop.SetValue(retval, resolver.GetById(relType, btId), null);
             *      //throw new ApplicationException(String.Format("Could not assign BelongsTo property \"{0}\" on object of type {1} by ID {2} because no object of type {3} could be retrieved by that ID.", prop.Name, objectType, btId, prop.PropertyType));
             *  }
             * }
             */

            return(retval);
        }
        protected void Serialize(object value, Stream writeStream, JsonWriter writer, JsonSerializer serializer, RelationAggregator aggregator)
        {
            writer.WriteStartObject();

            // The spec no longer requires that the ID key be "id":
            //     "An ID SHOULD be represented by an 'id' key..." :-/
            // But Ember Data does. So, we'll add "id" to the document
            // always, and also serialize the property under its given
            // name, for now at least.
            //TODO: Partly because of this, we should probably disallow updates to Id properties where practical.

            // Do the Id now...
            writer.WritePropertyName("id");
            var idProp = _modelManager.GetIdProperty(value.GetType());

            writer.WriteValue(GetValueForIdProperty(idProp, value));

            // Leverage the cached map to avoid another costly call to System.Type.GetProperties()
            PropertyInfo[] props = _modelManager.GetProperties(value.GetType());

            // Do non-model properties first, everything else goes in "links"
            //TODO: Unless embedded???
            IList <PropertyInfo> modelProps = new List <PropertyInfo>();

            foreach (PropertyInfo prop in props)
            {
                string propKey = _modelManager.GetJsonKeyForProperty(prop);
                if (propKey == "id")
                {
                    continue;                  // Don't write the "id" property twice, see above!
                }
                if (prop.PropertyType.CanWriteAsJsonApiAttribute())
                {
                    if (prop.GetCustomAttributes().Any(attr => attr is JsonIgnoreAttribute))
                    {
                        continue;
                    }

                    // numbers, strings, dates...
                    writer.WritePropertyName(propKey);

                    var propertyValue = prop.GetValue(value, null);

                    if (prop.PropertyType == typeof(Decimal) || prop.PropertyType == typeof(Decimal?))
                    {
                        if (propertyValue == null)
                        {
                            writer.WriteNull();
                        }
                        else
                        {
                            writer.WriteValue(propertyValue.ToString());
                        }
                    }
                    else if (prop.PropertyType == typeof(string) &&
                             prop.GetCustomAttributes().Any(attr => attr is SerializeStringAsRawJsonAttribute))
                    {
                        if (propertyValue == null)
                        {
                            writer.WriteNull();
                        }
                        else
                        {
                            var json = (string)propertyValue;
                            if (ValidateRawJsonStrings)
                            {
                                try
                                {
                                    var token = JToken.Parse(json);
                                    json = token.ToString();
                                }
                                catch (Exception)
                                {
                                    json = "{}";
                                }
                            }
                            var valueToSerialize = JsonHelpers.MinifyJson(json);
                            writer.WriteRawValue(valueToSerialize);
                        }
                    }
                    else
                    {
                        serializer.Serialize(writer, propertyValue);
                    }
                }
                else
                {
                    modelProps.Add(prop);
                    continue;
                }
            }

            // Now do other stuff
            if (modelProps.Count() > 0)
            {
                writer.WritePropertyName("links");
                writer.WriteStartObject();
            }
            foreach (PropertyInfo prop in modelProps)
            {
                bool               skip = false, iip = false;
                string             lt = null;
                SerializeAsOptions sa = SerializeAsOptions.Ids;

                object[] attrs = prop.GetCustomAttributes(true);

                foreach (object attr in attrs)
                {
                    Type attrType = attr.GetType();
                    if (typeof(JsonIgnoreAttribute).IsAssignableFrom(attrType))
                    {
                        skip = true;
                        continue;
                    }
                    if (typeof(IncludeInPayload).IsAssignableFrom(attrType))
                    {
                        iip = ((IncludeInPayload)attr).Include;
                    }
                    if (typeof(SerializeAs).IsAssignableFrom(attrType))
                    {
                        sa = ((SerializeAs)attr).How;
                    }
                    if (typeof(LinkTemplate).IsAssignableFrom(attrType))
                    {
                        lt = ((LinkTemplate)attr).TemplateString;
                    }
                }
                if (skip)
                {
                    continue;
                }

                writer.WritePropertyName(_modelManager.GetJsonKeyForProperty(prop));

                // Look out! If we want to SerializeAs a link, computing the property is probably
                // expensive...so don't force it just to check for null early!
                if (sa != SerializeAsOptions.Link && prop.GetValue(value, null) == null)
                {
                    writer.WriteNull();
                    continue;
                }

                // Now look for enumerable-ness:
                if (typeof(IEnumerable <Object>).IsAssignableFrom(prop.PropertyType))
                {
                    switch (sa)
                    {
                    case SerializeAsOptions.Ids:
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        IEnumerable <object> items = (IEnumerable <object>)prop.GetValue(value, null);
                        if (items == null)
                        {
                            writer.WriteValue((IEnumerable <object>)null); //TODO: Is it okay with the spec and Ember Data to return null for an empty array?
                            break;                                         // LOOK OUT! Ending this case block early here!!!
                        }
                        this.WriteIdsArrayJson(writer, items, serializer);
                        if (iip)
                        {
                            Type itemType;
                            if (prop.PropertyType.IsGenericType)
                            {
                                itemType = prop.PropertyType.GetGenericArguments()[0];
                            }
                            else
                            {
                                // Must be an array at this point, right??
                                itemType = prop.PropertyType.GetElementType();
                            }
                            if (aggregator != null)
                            {
                                aggregator.Add(itemType, items);                         // should call the IEnumerable one...right?
                            }
                        }
                        break;

                    case SerializeAsOptions.Link:
                        if (lt == null)
                        {
                            throw new JsonSerializationException("A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                        }
                        //TODO: Check for "{0}" in linkTemplate and (only) if it's there, get the Ids of all objects and "implode" them.
                        string href = String.Format(lt, null, GetIdFor(value));
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        //TODO: Support ids and type properties in "link" object
                        writer.WriteStartObject();
                        writer.WritePropertyName("href");
                        writer.WriteValue(href);
                        writer.WriteEndObject();
                        break;

                    case SerializeAsOptions.Embedded:
                        // Not really supported by Ember Data yet, incidentally...but easy to implement here.
                        //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                        //serializer.Serialize(writer, prop.GetValue(value, null));
                        this.Serialize(prop.GetValue(value, null), writeStream, writer, serializer, aggregator);
                        break;
                    }
                }
                else
                {
                    var propertyValue = prop.GetValue(value, null);
                    if (propertyValue == null)
                    {
                        writer.WriteNull();
                    }
                    else
                    {
                        string objId = GetIdFor(propertyValue);

                        switch (sa)
                        {
                        case SerializeAsOptions.Ids:
                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            serializer.Serialize(writer, objId);
                            if (iip)
                            {
                                if (aggregator != null)
                                {
                                    aggregator.Add(prop.PropertyType, prop.GetValue(value, null));
                                }
                            }
                            break;

                        case SerializeAsOptions.Link:
                            if (lt == null)
                            {
                                throw new JsonSerializationException(
                                          "A property was decorated with SerializeAs(SerializeAsOptions.Link) but no LinkTemplate attribute was provided.");
                            }
                            string link = String.Format(lt, objId,
                                                        GetIdFor(value)); //value.GetType().GetProperty("Id").GetValue(value, null));

                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            writer.WriteStartObject();
                            writer.WritePropertyName("href");
                            writer.WriteValue(link);
                            writer.WriteEndObject();
                            break;

                        case SerializeAsOptions.Embedded:
                            // Not really supported by Ember Data yet, incidentally...but easy to implement here.
                            //writer.WritePropertyName(ContractResolver._modelManager.GetJsonKeyForProperty(prop));
                            //serializer.Serialize(writer, prop.GetValue(value, null));
                            this.Serialize(prop.GetValue(value, null), writeStream, writer, serializer, aggregator);
                            break;
                        }
                    }
                }
            }
            if (modelProps.Count() > 0)
            {
                writer.WriteEndObject();
            }

            writer.WriteEndObject();
        }