/// <summary>
        /// Deserializes the specified json string.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="currentReadIndex">Index of the current read.</param>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public override object Deserialize(string json, ref int currentReadIndex, SerializationContext context)
        {
            // check if value is null
            int expectedSepIndex = currentReadIndex + keyLength;

            if (json[expectedSepIndex] == 'n' &&
                json[expectedSepIndex + 1] == 'u' &&
                json[expectedSepIndex + 2] == 'l' &&
                json[expectedSepIndex + 3] == 'l')
            {
                currentReadIndex = expectedSepIndex + 4;
                return(null);
            }

            if (json[expectedSepIndex + 1] == Structure.CharQuotationMark &&
                json[expectedSepIndex + 2] == '#' &&
                json[expectedSepIndex + 3] == 't' &&
                json[expectedSepIndex + 4] == 'y' &&
                json[expectedSepIndex + 5] == 'p' &&
                json[expectedSepIndex + 6] == 'e'
                )
            {
                return(DeserializeSpecialType(json, ref currentReadIndex, context, expectedSepIndex));
            }


            if (isObject)
            {
                Type objectTargetType = null;
                if (unknownTypeResolver != null)
                {
                    context.Key             = this.key;
                    context.ValueStartIndex = currentReadIndex;
                    context.ArrayIndex      = null;
                    context.JsonString      = json;
                    if (type.IsInterface)
                    {
                        context.InterfaceType = this.type;
                    }

                    objectTargetType = unknownTypeResolver(context);

                    if (context.InterfaceType != null)
                    {
                        context.InterfaceType = null;
                    }
                }

                if (objectTargetType == null)
                {
                    objectTargetType = Structure.GetDefaultType(json, expectedSepIndex);

                    if (objectTargetType == null)
                    {
                        throw new InvalidOperationException("Can't determine target type for JSON key: \"" + context.Key + "\"");
                    }
                }

                IJsonTypeStructure currentObjectStructure;
                if (!typeSerializerCache.TryGetValue(objectTargetType, out currentObjectStructure))
                {
                    currentObjectStructure = Structure.DetermineStructure(objectTargetType, this.key, context, this.isArrayItem);
                    typeSerializerCache[objectTargetType] = currentObjectStructure;
                }

                return(currentObjectStructure.Deserialize(json, ref currentReadIndex, context));
            }
            else
            {
                if (json[currentReadIndex] != Structure.CharLeftBrace &&
                    !isArrayItem)
                {
                    if (json[currentReadIndex] == Structure.CharComma)
                    {
                        currentReadIndex += 1;
                    }

                    expectedSepIndex = currentReadIndex + keyLength - 1;

                    if (json[expectedSepIndex] == Structure.CharColon)
                    {
                        currentReadIndex = expectedSepIndex + 1;
                    }
                    else
                    {
                        throw new Exception("Unexpected JSON data!");
                    }
                }

                if (this.concreteTargetType == null)
                {
                    // try get static target type
                    if (type.IsInterface || type.IsAbstract)
                    {
                        context.InterfaceType = type;
                        context.Key           = key;

                        Type itemTypeResolved = unknownTypeResolver(context);
                        if (itemTypeResolved != null)
                        {
                            concreteTargetType = itemTypeResolved;
                        }
                        context.InterfaceType = null;
                    }
                    else
                    {
                        concreteTargetType = type;
                    }
                }

                // create target object
                object target;
                try
                {
                    target = TypeService.CreateInstance(this.concreteTargetType);
                }
                catch (Exception ex)
                {
                    throw new Exception("Can't create object instance for \"" + this.concreteTargetType + "\" - \"" + type + "\"!", ex);
                }
                object oldParentObj = context.ParentObject;
                context.ParentObject = target;

                // jump over opening brace
                currentReadIndex += 1;

                for (int propIndex = 0; propIndex < objectStructure.Length; propIndex++)
                {
                    IJsonTypeStructure structure = objectStructure[propIndex];
                    var setPropertyAccessor      = setAccessorByPropertyIndex[propIndex];

                    // check out of order json
                    int expectedKeyStartIndex = currentReadIndex + 1;
                    int actualKeyIndex        = json.IndexOf(structure.Key, expectedKeyStartIndex);
                    if (actualKeyIndex != expectedKeyStartIndex ||
                        json[structure.Key.Length + actualKeyIndex] != Structure.CharQuotationMark)     // Recognize key with the same begin string but different ending
                    {
                        // out of order key recognized


                        int keyEndIndex       = json.IndexOf(Structure.CharQuotationMark, expectedKeyStartIndex);
                        int?keyStructureIndex = null;
                        StructureComplexOutOfOrder cachedOutOfOrderStruct = null;
                        string actualKey = null;
                        if (keyEndIndex != -1)  // ignore if end of json is reached
                        {
                            actualKey = json.Substring(expectedKeyStartIndex, keyEndIndex - expectedKeyStartIndex);

                            // check if out of order structure is already cached
                            if (outOfOrderStructures != null)
                            {
                                for (int indexCachedOutOfOrder = 0; indexCachedOutOfOrder < outOfOrderStructures.Count; indexCachedOutOfOrder++)
                                {
                                    var cachedItem = outOfOrderStructures[indexCachedOutOfOrder];

                                    if (cachedItem.ObjectStructure[propIndex].Key == actualKey)
                                    {
                                        cachedOutOfOrderStruct = cachedItem;
                                        break;
                                    }
                                }
                            }

                            if (cachedOutOfOrderStruct == null)
                            {
                                // find out of order key
                                keyStructureIndex = FindOutOfOrderKey(objectStructure, actualKey);
                            }
                        }

                        if (cachedOutOfOrderStruct != null)
                        {
                            structure           = cachedOutOfOrderStruct.ObjectStructure[propIndex];
                            setPropertyAccessor = cachedOutOfOrderStruct.SetAccessorByPropertyIndex[propIndex];
                        }
                        else if (keyStructureIndex.HasValue)
                        {
                            lock (syncObj)
                            {
                                // create or move out of order structure

                                if (outOfOrderStructures == null)
                                {
                                    outOfOrderStructures = new List <StructureComplexOutOfOrder>();
                                }

                                // try reuse existing out of order array
                                StructureComplexOutOfOrder currentOutOfOrderStruct;
                                var existing = outOfOrderStructures.Where(ooo => ooo.MaxTargetIndex < propIndex).FirstOrDefault();

                                IJsonTypeStructure[]      sourceStructure = objectStructure;
                                Func <object, object>[]   sourceGetAccessorByPropertyIndex = getAccessorByPropertyIndex;
                                Action <object, object>[] sourceSetAccessorByPropertyIndex = setAccessorByPropertyIndex;

                                // create new cloned out of order strucutre
                                if (existing == null)
                                {
                                    currentOutOfOrderStruct = new StructureComplexOutOfOrder()
                                    {
                                        ObjectStructure            = (IJsonTypeStructure[])sourceStructure.Clone(),
                                        GetAccessorByPropertyIndex = (Func <object, object>[])sourceGetAccessorByPropertyIndex.Clone(),
                                        SetAccessorByPropertyIndex = (Action <object, object>[])sourceSetAccessorByPropertyIndex.Clone()
                                    };
                                }
                                else
                                {
                                    currentOutOfOrderStruct = existing;
                                    sourceStructure         = existing.ObjectStructure;
                                    structure = sourceStructure[propIndex];
                                    sourceGetAccessorByPropertyIndex = existing.GetAccessorByPropertyIndex;
                                    sourceSetAccessorByPropertyIndex = existing.SetAccessorByPropertyIndex;

                                    // search in chached object structure again
                                    keyStructureIndex = FindOutOfOrderKey(sourceStructure, actualKey);
                                }

                                // switch object structure to received json order
                                IJsonTypeStructure foundOutOfOrderStructure = sourceStructure[keyStructureIndex.Value];
                                currentOutOfOrderStruct.ObjectStructure[propIndex] = foundOutOfOrderStructure;
                                currentOutOfOrderStruct.ObjectStructure[keyStructureIndex.Value] = structure;
                                structure = foundOutOfOrderStructure;
                                currentOutOfOrderStruct.MaxTargetIndex = propIndex;

                                // switch value setter
                                var currentAccessor         = sourceGetAccessorByPropertyIndex[propIndex];
                                var foundOutOfOrderAccessor = sourceGetAccessorByPropertyIndex[keyStructureIndex.Value];
                                currentOutOfOrderStruct.GetAccessorByPropertyIndex[propIndex] = foundOutOfOrderAccessor;
                                currentOutOfOrderStruct.GetAccessorByPropertyIndex[keyStructureIndex.Value] = currentAccessor;

                                var currentAccessorSet         = sourceSetAccessorByPropertyIndex[propIndex];
                                var foundOutOfOrderAccessorSet = sourceSetAccessorByPropertyIndex[keyStructureIndex.Value];
                                currentOutOfOrderStruct.SetAccessorByPropertyIndex[propIndex] = foundOutOfOrderAccessorSet;
                                currentOutOfOrderStruct.SetAccessorByPropertyIndex[keyStructureIndex.Value] = currentAccessorSet;

                                setPropertyAccessor = foundOutOfOrderAccessorSet;

                                if (existing == null)
                                {
                                    outOfOrderStructures.Add(currentOutOfOrderStruct);
                                }
                            }
                        }
                        else
                        {
                            if (context.Serializer.IsMissingFieldDataAllowed)
                            {
                                if (SkipJsonElement(json, ref currentReadIndex))
                                {
                                    propIndex--; // re-read current structure
                                }
                                continue;        // ignore missing field
                            }
                            else
                            {
                                throw new KeyNotFoundException(string.Format("The key: \"{0}\" is not present in the object structure! JSON: \"{1}\"; Target Type: \"{2}\"", actualKey, json, this.type.AssemblyQualifiedName));
                            }
                        }
                    }

                    object value = structure.Deserialize(json, ref currentReadIndex, context);

                    if (value != null)
                    {
                        setPropertyAccessor(target, value);
                    }

                    if (json[currentReadIndex] == Structure.CharRightBrace)
                    {
                        // end reached
                        currentReadIndex++;
                        break;
                    }

                    currentReadIndex++;
                }

                if (json[currentReadIndex - 1] != Structure.CharRightBrace)
                {
                    if (context.Serializer.IsMissingFieldDataAllowed)
                    {
                        // json object contains more data than expected from the type structure
                        // skip futher object data
                        SkipJsonObject(json, ref currentReadIndex);
                    }
                    else
                    {
                        throw new InvalidOperationException(string.Format("The json object contains more data than expected in target type! JSON: \"{0}\"; Key: \"{1}\"; Target Type: \"{2}\"", json, this.key, this.type.AssemblyQualifiedName));
                    }
                }

                context.ParentObject = oldParentObj;

                return(target);
            }
        }
Exemple #2
0
        private object DeserializeArrayItem(string json, ref int currentReadIndex, int arrayItemIndex, SerializationContext context)
        {
            if (json[currentReadIndex] == 'n' &&
                json[currentReadIndex + 1] == 'u' &&
                json[currentReadIndex + 2] == 'l' &&
                json[currentReadIndex + 3] == 'l')
            {
                currentReadIndex = currentReadIndex + 4;
                return(null);
            }

            //Type objectTargetType = null;
            if (json[currentReadIndex + 2] == '#' &&
                json[currentReadIndex + 3] == 't' &&
                json[currentReadIndex + 4] == 'y' &&
                json[currentReadIndex + 5] == 'p' &&
                json[currentReadIndex + 6] == 'e'
                )
            {
                return(DeserializeSpecialType(json, ref currentReadIndex, context, currentReadIndex));
            }

            object itemValue;

            if (this.isObject)
            {
                // determine target type during deserialization
                Type type = null;
                if (unknownTypeResolver != null)
                {
                    context.Key             = this.key;
                    context.ValueStartIndex = currentReadIndex;
                    context.ArrayIndex      = arrayItemIndex;
                    context.JsonString      = json;

                    type = unknownTypeResolver(context);

                    context.ArrayIndex = null;
                }

                if (type == null || type.Equals(typeof(object)))
                {
                    type = Structure.GetDefaultType(json, currentReadIndex);

                    if (type == null)
                    {
                        int    readCount = Math.Min(json.Length - currentReadIndex, 25);
                        string jsonPart  = json.Substring(currentReadIndex, readCount);

                        throw new InvalidOperationException(string.Format("Unable to get the target type for array object index {0}; Key: {1}; JSON part: \"{2}\"", arrayItemIndex, this.key, jsonPart));
                    }
                }

                IJsonTypeStructure currentObjectStructure;
                if (!typeSerializerCache.TryGetValue(type, out currentObjectStructure))
                {
                    currentObjectStructure    = Structure.DetermineStructure(type, GetNestedArrayKey(this.key, arrayItemIndex), context, true);
                    typeSerializerCache[type] = currentObjectStructure;
                }

                itemValue = currentObjectStructure.Deserialize(json, ref currentReadIndex, context);
            }
            else
            {
                if (itemDeSerializer == null)
                {
                    itemDeSerializer = Structure.DetermineStructure(itemType, key, context, true);
                }

                // fixed array target type
                itemValue = itemDeSerializer.Deserialize(json, ref currentReadIndex, context);
            }

            return(itemValue);
        }