static CodeBlock WriteNode( Variable <JsonWriter> jsonWriter, TypedExpression <string> baseUri, ResourceModel model, Expression resource, MemberAccess <Func <object, string> > uriGenerator, MemberAccess <Func <object, string> > typeGenerator, IMetaModelRepository models, Variable <HydraJsonFormatterResolver> jsonFormatterResolver, NodeProperty[] existingNodeProperties = null, Stack <ResourceModel> recursionDefender = null) { recursionDefender = recursionDefender ?? new Stack <ResourceModel>(); var resourceType = model.ResourceType; if (recursionDefender.Contains(model)) { throw new InvalidOperationException( $"Detected recursion, already processing {resourceType?.Name}: {string.Join("->", recursionDefender.Select(m => m.ResourceType?.Name).Where(n => n != null))}"); } recursionDefender.Push(model); var resourceRegistrationHydraType = HydraTextExtensions.GetHydraTypeName(model); var resourceUri = uriGenerator.Invoke(resource); List <NodeProperty> nodeProperties = new List <NodeProperty>(existingNodeProperties ?? Enumerable.Empty <NodeProperty>()); nodeProperties.AddRange(GetNodeProperties( jsonWriter, baseUri, model, resource, uriGenerator, typeGenerator, models, recursionDefender, jsonFormatterResolver, resourceType, resourceUri, resourceRegistrationHydraType)); var collectionItemTypes = HydraTextExtensions.CollectionItemTypes(resourceType).ToList(); Type collectionItemType = null; var isHydraCollection = collectionItemTypes.Count == 1 && models.TryGetResourceModel(collectionItemType = collectionItemTypes.First(), out _); IEnumerable <AnyExpression> render() { if (isHydraCollection) { var collectionType = HydraTypes.Collection.MakeGenericType(collectionItemType); var collectionCtor = collectionType.GetConstructor(new[] { typeof(IEnumerable <>).MakeGenericType(collectionItemType) }); var collection = Expression.Variable(collectionType); yield return(collection); var instantiateCollection = Expression.Assign(collection, Expression.New(collectionCtor, resource)); yield return(instantiateCollection); resource = collection; // if we have a generic list of sort, we hydra:Collection instead if (resourceType.IsGenericType) // IEnum<T>, List<T> etc { resourceRegistrationHydraType = "hydra:Collection"; } resourceType = collectionType; // Remove existing id and type if already defined nodeProperties.RemoveAll(p => p.Name == "@id" || p.Name == "@type"); nodeProperties.AddRange(GetNodeProperties(jsonWriter, baseUri, model, resource, uriGenerator, typeGenerator, models, recursionDefender, jsonFormatterResolver, resourceType, resourceUri, resourceRegistrationHydraType)); } if (nodeProperties.Any()) { yield return(WriteNodeProperties(jsonWriter, nodeProperties)); } } recursionDefender.Pop(); return(new CodeBlock(render())); }
static IEnumerable <NodeProperty> GetNodeProperties( Variable <JsonWriter> jsonWriter, TypedExpression <string> baseUri, ResourceModel model, Expression resource, MemberAccess <Func <object, string> > uriGenerator, MemberAccess <Func <object, string> > typeGenerator, IMetaModelRepository models, Stack <ResourceModel> recursionDefender, Variable <HydraJsonFormatterResolver> jsonFormatterResolver, Type resourceType, TypedExpression <string> resourceUri, string resourceRegistrationHydraType) { var publicProperties = resourceType .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(HydraTextExtensions.IsNotIgnored) .ToList(); var propNames = publicProperties.Select(HydraTextExtensions.GetJsonPropertyName); var overridesId = propNames.Any(name => name == "@id"); var overridesType = propNames.Any(name => name == "@type"); if (overridesId == false && model.Uris.Any()) { yield return(WriteId(jsonWriter, resourceUri)); } if (overridesType == false) { var typePropertyFactory = model.Hydra().TypeFunc; if (typePropertyFactory == null) { yield return(WriteType(jsonWriter, resourceRegistrationHydraType)); } else { yield return(WriteType(jsonWriter, StringMethods.Concat(baseUri, typeGenerator.Invoke(resource)))); } } foreach (var pi in publicProperties) { if (pi.GetIndexParameters().Any()) { continue; } if (pi.PropertyType == typeof(string) || (pi.PropertyType.IsValueType && Nullable.GetUnderlyingType(pi.PropertyType) == null)) { var nodePropertyValue = WriteNodePropertyValue( jsonWriter, pi, jsonFormatterResolver, resource); yield return(nodePropertyValue); continue; } yield return(WriteNodeProperty( jsonWriter, baseUri, resource, uriGenerator, typeGenerator, models, recursionDefender, pi, jsonFormatterResolver)); } foreach (var link in model.Links) { yield return(WriteNodeLink(jsonWriter, link.Relationship, link.Uri, resourceUri, link)); } }