public static void ResourceDocument(ParameterExpression jsonWriter,
                                            ResourceModel model,
                                            Expression resource,
                                            Expression options,
                                            Action <ParameterExpression> variable,
                                            Action <Expression> statement, IMetaModelRepository models)
        {
            var uriResolverFunc = MakeMemberAccess(options, SerializationContextUriResolverPropertyInfo);

            var contextUri = StringConcat(
                Call(MakeMemberAccess(options, SerializationContextBaseUriPropertyInfo),
                     typeof(object).GetMethod(nameof(ToString))),
                Constant(".hydra/context.jsonld"));

            var resolver = Variable(typeof(CustomResolver), "resolver");

            variable(resolver);
            statement(Assign(resolver, New(typeof(CustomResolver))));

            foreach (var exp in WriteBeginObjectContext(jsonWriter, contextUri))
            {
                statement(exp);
            }

            WriteNode(jsonWriter, model, resource, variable, statement, uriResolverFunc, models, new Stack <ResourceModel>(),
                      resolver);

            statement(JsonWriterMethods.WriteEndObject(jsonWriter));
        }
        static void WriteNodeProperty(ParameterExpression jsonWriter, Expression resource,
                                      Action <ParameterExpression> variable, Action <Expression> statement,
                                      MemberExpression uriResolverFunc, IMetaModelRepository models, Stack <ResourceModel> recursionDefender,
                                      PropertyInfo pi,
                                      ParameterExpression resolver)
        {
            var propertyStatements = new List <Expression>();
            var propertyVars       = new List <ParameterExpression>();

            // var propertyValue;
            var propertyValue = Variable(pi.PropertyType, $"val{pi.DeclaringType.Name}{pi.Name}");

            variable(propertyValue);

            // propertyValue = resource.Property;
            statement(Assign(propertyValue, MakeMemberAccess(resource, pi)));


            if (models.TryGetResourceModel(pi.PropertyType, out var propertyResourceModel))
            {
                // property has a registration, it's either an iri node or a blank node
                propertyStatements.Add(JsonWriterMethods.WriteValueSeparator(jsonWriter));
                propertyStatements.Add(WritePropertyName(jsonWriter, GetJsonPropertyName(pi)));
                propertyStatements.Add(JsonWriterMethods.WriteBeginObject(jsonWriter));
                WriteNode(jsonWriter, propertyResourceModel, propertyValue, propertyVars.Add, propertyStatements.Add,
                          uriResolverFunc, models, recursionDefender, resolver);
                propertyStatements.Add(JsonWriterMethods.WriteEndObject(jsonWriter));
            }
            else
            {
                // not an iri node itself, but is it a list of nodes?
                var itemResourceRegistrations = (
                    from i in pi.PropertyType.GetInterfaces()
                    where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable <>)
                    let itemType = i.GetGenericArguments()[0]
                                   where itemType != typeof(object)
                                   let resourceModels = models.ResourceRegistrations.Where(r => itemType.IsAssignableFrom(r.ResourceType))
                                                        where resourceModels.Any()
                                                        orderby resourceModels.Count() descending
                                                        select new
                {
                    itemType, models =
                        (from possible in resourceModels
                         orderby possible.ResourceType.GetInheritanceDistance(itemType)
                         select possible).ToList()
                }).FirstOrDefault();

                if (itemResourceRegistrations == null)
                {
                    // not a list of iri or blank nodes
                    WriteNodePropertyValue(jsonWriter, propertyVars.Add, propertyStatements.Add, pi, resolver,
                                           MakeMemberAccess(resource, pi));
                }
                else
                {
                    // it's a list of nodes
                    var itemArrayType = itemResourceRegistrations.itemType.MakeArrayType();
                    var itemArray     = Variable(itemArrayType);

                    var toArrayMethod = typeof(Enumerable).GetMethod("ToArray")
                                        .MakeGenericMethod(itemResourceRegistrations.itemType);
                    var assign = Assign(itemArray, Call(toArrayMethod, propertyValue));
                    propertyVars.Add(itemArray);
                    propertyStatements.Add(assign);

                    var i = Variable(typeof(int));
                    propertyVars.Add(i);

                    var initialValue = Assign(i, Constant(0));
                    propertyStatements.Add(initialValue);

                    var itemVars       = new List <ParameterExpression>();
                    var itemStatements = new List <Expression>();

                    var @break = Label("break");

                    propertyStatements.Add(JsonWriterMethods.WriteValueSeparator(jsonWriter));
                    propertyStatements.Add(WritePropertyName(jsonWriter, GetJsonPropertyName(pi)));

                    propertyStatements.Add(JsonWriterMethods.WriteBeginArray(jsonWriter));

                    itemStatements.Add(IfThen(GreaterThan(i, Constant(0)), JsonWriterMethods.WriteValueSeparator(jsonWriter)));
                    itemStatements.Add(JsonWriterMethods.WriteBeginObject(jsonWriter));

                    BlockExpression resourceBlock(ResourceModel r, ParameterExpression typed)
                    {
                        var vars       = new List <ParameterExpression>();
                        var statements = new List <Expression>();

                        WriteNode(
                            jsonWriter,
                            r,
                            typed,
                            vars.Add, statements.Add,
                            uriResolverFunc, models, recursionDefender, resolver);
                        return(Block(vars.ToArray(), statements.ToArray()));
                    }

                    Expression renderBlock = Block(Throw(New(typeof(InvalidOperationException))));

                    // with C : B : A, if is C else if is B else if is A else throw

                    foreach (var specificModel in itemResourceRegistrations.models)
                    {
                        var typed = Variable(specificModel.ResourceType, "as" + specificModel.ResourceType.Name);
                        itemVars.Add(typed);
                        var @as = Assign(typed, TypeAs(ArrayAccess(itemArray, i), specificModel.ResourceType));
                        renderBlock = IfThenElse(
                            NotEqual(@as, Default(specificModel.ResourceType)),
                            resourceBlock(specificModel, @typed),
                            renderBlock);
                    }

                    itemStatements.Add(renderBlock);
                    itemStatements.Add(PostIncrementAssign(i));
                    itemStatements.Add(JsonWriterMethods.WriteEndObject(jsonWriter));
                    var loop = Loop(
                        IfThenElse(
                            LessThan(i, MakeMemberAccess(itemArray, itemArrayType.GetProperty("Length"))),
                            Block(itemVars.ToArray(), itemStatements.ToArray()),
                            Break(@break)),
                        @break
                        );
                    propertyStatements.Add(loop);
                    propertyStatements.Add(JsonWriterMethods.WriteEndArray(jsonWriter));
                }
            }

            statement(IfThen(
                          NotEqual(propertyValue, Default(pi.PropertyType)),
                          Block(propertyVars.ToArray(), propertyStatements.ToArray())));
        }