private void DeserializeDictionaryGeneric <TValue>(IDeserializerNode node,
                                                           Reader reader,
                                                           DictionaryTypeSpec dictType)
        {
            IDictionary <string, TValue> dict;

            if (node.Value != null)
            {
                dict = (IDictionary <string, TValue>)node.Value;
            }
            else
            {
                dict = new Dictionary <string, TValue>();
            }

            var jobj = reader.Token as JObject;

            var valueType = dictType.ValueType;

            if (jobj == null)
            {
                throw new PomonaSerializationException(
                          "Expected dictionary property to have a JSON object value, but was " + reader.Token.Type);
            }

            foreach (var jprop in jobj.Properties())
            {
                var jpropName = jprop.Name;
                if (jpropName.Length > 0 && reservedFirstCharacters.Contains(jpropName[0]))
                {
                    if (jpropName[0] == '-')
                    {
                        // Removal operation
                        var unescapedPropertyName = UnescapePropertyName(jpropName.Substring(1));
                        dict.Remove(unescapedPropertyName);
                    }
                    else
                    {
                        throw new PomonaSerializationException(
                                  "Unexpected character in json property name. Have propertie names been correctly escaped?");
                    }
                }
                else
                {
                    var unescapedPropertyName = UnescapePropertyName(jpropName);
                    var itemNode = new ItemValueDeserializerNode(valueType,
                                                                 node.Context,
                                                                 node.ExpandPath + "." + unescapedPropertyName);
                    DeserializeThroughContext(itemNode, new Reader(jprop.Value));
                    dict[unescapedPropertyName] = (TValue)itemNode.Value;
                }
            }

            if (node.Value == null)
            {
                node.Value = dict;
            }
        }
        private object Deserialize(TextReader textReader,
                                   TypeSpec expectedBaseType,
                                   IDeserializationContext context,
                                   object patchedObject)
        {
            var node = new ItemValueDeserializerNode(expectedBaseType, context);

            node.Operation = patchedObject != null ? DeserializerNodeOperation.Patch : DeserializerNodeOperation.Post;
            if (patchedObject != null)
            {
                node.Value = patchedObject;
            }

            var reader = CreateReader(textReader);

            DeserializeThroughContext(node, reader);
            return(node.Value);
        }
        private void DeserializeArrayNodeGeneric <TElement>(IDeserializerNode node, Reader reader)
        {
            if (TryDeserializeAsReference(node, reader))
            {
                return;
            }

            var jarr = reader.Token as JArray;

            if (jarr == null)
            {
                throw new PomonaSerializationException("Expected JSON token of type array, but was " + reader.Token.Type);
            }

            var expectedBaseType = node.ExpectedBaseType;

            // Deserialize as object array by default
            bool     asArray;
            TypeSpec elementType;

            if (expectedBaseType != null && expectedBaseType.IsCollection)
            {
                elementType = expectedBaseType.ElementType;
                asArray     = expectedBaseType.IsArray;
            }
            else
            {
                elementType = node.Context.GetClassMapping(typeof(object));
                asArray     = true;
            }

            bool isPatching;
            ICollection <TElement> collection;

            if (node.Value == null)
            {
                if (expectedBaseType != null && expectedBaseType == typeof(ISet <TElement>))
                {
                    collection = new HashSet <TElement>();
                }
                else
                {
                    collection = new List <TElement>();
                }
                isPatching = false;
            }
            else
            {
                collection = (ICollection <TElement>)node.Value;
                isPatching = true;
            }

            if (isPatching && node.Operation == DeserializerNodeOperation.Post)
            {
                // Clear list and add new items
                node.CheckItemAccessRights(HttpMethod.Delete);
                collection.Clear();
            }

            foreach (var jitem in jarr)
            {
                var jobj     = jitem as JObject;
                var itemNode = new ItemValueDeserializerNode(elementType, node.Context, node.ExpandPath, node);
                if (jobj != null)
                {
                    foreach (var jprop in jobj.Properties().Where(IsIdentifierProperty))
                    {
                        // Starts with "-@" or "*@"
                        var identifyPropName = jprop.Name.Substring(2);
                        var identifyProp     =
                            itemNode.ValueType.Properties.FirstOrDefault(x => x.JsonName == identifyPropName);
                        if (identifyProp == null)
                        {
                            throw new PomonaSerializationException("Unable to find predicate property " + jprop.Name
                                                                   + " in object");
                        }

                        var identifierNode = new ItemValueDeserializerNode(identifyProp.PropertyType,
                                                                           itemNode.Context,
                                                                           parent: itemNode);
                        DeserializeThroughContext(identifierNode, new Reader(jprop.Value));
                        var identifierValue = identifierNode.Value;

                        if (jprop.Name[0] == '-')
                        {
                            itemNode.Operation = DeserializerNodeOperation.Delete;
                        }
                        else if (jprop.Name[0] == '*')
                        {
                            itemNode.Operation = DeserializerNodeOperation.Patch;
                        }
                        else
                        {
                            throw new PomonaSerializationException("Unexpected json patch identifier property.");
                        }
                        itemNode.Value =
                            collection.Cast <object>().First(
                                x => identifierValue.Equals(identifyProp.GetValue(x, itemNode.Context)));
                    }
                }

                if (itemNode.Operation == DeserializerNodeOperation.Delete)
                {
                    node.CheckItemAccessRights(HttpMethod.Delete);
                    collection.Remove((TElement)itemNode.Value);
                }
                else
                {
                    if (itemNode.Operation == DeserializerNodeOperation.Patch)
                    {
                        node.CheckItemAccessRights(HttpMethod.Patch);
                    }
                    else if (isPatching)
                    {
                        node.CheckAccessRights(HttpMethod.Post);
                    }

                    DeserializeThroughContext(itemNode, new Reader(jitem));
                    if (itemNode.Operation != DeserializerNodeOperation.Patch)
                    {
                        if (!(itemNode.ExpectedBaseType is StructuredType) ||
                            itemNode.ExpectedBaseType.IsAnonymous() ||
                            !collection.Contains((TElement)itemNode.Value))
                        {
                            collection.Add((TElement)itemNode.Value);
                        }
                    }
                }
            }

            if (node.Value == null)
            {
                node.Value = asArray ? collection.ToArray() : collection;
            }
        }