private PropertyTypeNode ParseContainer(
            IDictionary <string, object> t,
            ContainerTypeTreePath context,
            bool ignoreNamespace = false)
        {
            var containerTypeName = SafeGetKeyValue(t, JsonSchema.Keys.ContainerNameKey);

            if (string.IsNullOrWhiteSpace(containerTypeName))
            {
                throw new Exception(
                          $"Invalid container type (empty) while parsing json (path: '{context.FullPath}')");
            }

            if (!ignoreNamespace)
            {
                context.Namespace = SafeGetKeyValue(t, JsonSchema.Keys.NamespaceKey);
            }

            var generatedUserHooks =
                SafeGetKeyValue(t, JsonSchema.Keys.GeneratedUserHooksKey);

            var overrideDefaultBaseClass =
                SafeGetKeyValue(t, JsonSchema.Keys.OverrideDefaultBaseClassKey);

            bool isAbstractClass;

            if (!bool.TryParse(
                    SafeGetKeyValue(t, JsonSchema.Keys.IsAbstractClassKey),
                    out isAbstractClass))
            {
                isAbstractClass = PropertyTypeNode.Defaults.IsAbstractClass;
            }

            bool noDefaultImplementation;

            if (!bool.TryParse(
                    SafeGetKeyValue(t, JsonSchema.Keys.NoDefaultImplementationKey),
                    out noDefaultImplementation))
            {
                noDefaultImplementation = PropertyTypeNode.Defaults.NoDefaultImplementation;
            }

            var n = new PropertyTypeNode
            {
                TypePath  = new ContainerTypeTreePath(context),
                TypeName  = containerTypeName,
                Tag       = GetTypeTagFromPropertyName(containerTypeName, context),
                UserHooks = UserHooks.From(generatedUserHooks),
                OverrideDefaultBaseClass = overrideDefaultBaseClass,
                IsAbstractClass          = isAbstractClass,
                Properties = ParseProperties(t, context),
                NativeType = TryExtractPropertyNativeType(containerTypeName),
                NoDefaultImplementation = noDefaultImplementation,
            };

            if (t.ContainsKey(JsonSchema.Keys.ConstructedFromKey))
            {
                var constructorParams = t[JsonSchema.Keys.ConstructedFromKey] as IEnumerable;

                var paramTypes = new List <KeyValuePair <string, string> >();

                if (constructorParams != null)
                {
                    paramTypes.AddRange(from object p in constructorParams
                                        select p as IDictionary <string, object>
                                        into dp
                                        let paramType = dp.ContainsKey(JsonSchema.Keys.PropertyTypeKey)
                            ? (dp[JsonSchema.Keys.PropertyTypeKey] as string)
                            : ""
                                                        let paramName = dp.ContainsKey(JsonSchema.Keys.PropertyNameKey)
                            ? (dp[JsonSchema.Keys.PropertyNameKey] as string)
                            : ""
                                                                        where !string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramType)
                                                                        select new KeyValuePair <string, string>(paramType, paramName));
                }

                n.Constructor.ParameterTypes = paramTypes;
            }

            if (!t.ContainsKey(JsonSchema.Keys.NestedTypesKey) ||
                !(t[JsonSchema.Keys.NestedTypesKey] is IEnumerable))
            {
                return(n);
            }

            var nestedContainers = (IEnumerable)t[JsonSchema.Keys.NestedTypesKey];

            context.TypePath.Push(containerTypeName);

            foreach (var nestedContainerEnumerable in nestedContainers)
            {
                var nestedContainer = nestedContainerEnumerable as IDictionary <string, object>;
                if (nestedContainer == null)
                {
                    continue;
                }

                n.NestedContainers.Add(
                    ParseContainer(
                        nestedContainer,
                        context,
                        true
                        )
                    );
            }

            context.TypePath.Pop();

            return(n);
        }
        private static void ParseContainersSchema(IDictionary <string, object> d, List <PropertyTypeNode> definitions)
        {
            Assert.IsNotNull(definitions);

            var ns = d.ContainsKey(SerializedKeys.NamespaceKey) ? (d[SerializedKeys.NamespaceKey] as string) : "";

            var symbolsTable = new Dictionary <string, PropertyTypeNode>();

            if (d.ContainsKey(SerializedKeys.TypesKey))
            {
                // 1. First pass

                // Here we just check the meta info for the types
                // to fill some sort of a symbol table. Those meta info
                // will then useful in the case of
                // types cross referencing other types in that schema, so that
                // we know what that user defined type is (value type).

                // We could add am optional { "IsValueType": false } to all the subtypes
                // but it would be redundant (every time  a type could refer that another
                // it would duplicate that info ..).

                var types = d[SerializedKeys.TypesKey] as IEnumerable;
                if (types != null)
                {
                    foreach (var type in types)
                    {
                        var t = type as IDictionary <string, object>;
                        if (t != null)
                        {
                            var containerTypeName =
                                SafeGetKeyValue(t, SerializedKeys.ContainerNameKey);

                            var generatedUserHooks =
                                SafeGetKeyValue(t, SerializedKeys.GeneratedUserHooksKey);

                            var overrideDefaultBaseClass =
                                SafeGetKeyValue(t, SerializedKeys.OverrideDefaultBaseClassKey);

                            bool isAbstractClass;
                            if (!Boolean.TryParse(
                                    SafeGetKeyValue(t, SerializedKeys.IsAbstractClassKey),
                                    out isAbstractClass))
                            {
                                isAbstractClass = false;
                            }

                            var n = new PropertyTypeNode
                            {
                                Namespace = ns,
                                Name      = containerTypeName,
                                TypeName  = containerTypeName,
                                RawNode   = t,
                                Tag       = TypeTagForSymbol(t),
                                UserHooks = UserHooks.From(generatedUserHooks),
                                OverrideDefaultBaseClass = overrideDefaultBaseClass,
                                IsAbstractClass          = isAbstractClass
                            };

                            if (n.Name != null)
                            {
                                symbolsTable[n.Name] = n;
                            }
                        }
                    }
                }

                // 2. Second pass

                foreach (var symbol in symbolsTable)
                {
                    var node = symbol.Value;

                    ParsePropertyContainer(node, symbolsTable);

                    definitions.Add(node);
                }
            }
        }