private JObject GetMetadata(TreeRecord entity, UserAccess access)
        {
            if (entity?.Level == null)
            {
                return(null);
            }

            var type = modelData.GetEntityType(entity);

            if (type == null)
            {
                Log.Information($"No type found for entiy level {entity?.Level} and external template {entity.ExternalDisplayTemplateName}");
                return(null);
            }

            var language = access.Language ?? WebHelper.DefaultLanguage;

            JObject metadata     = null;
            var     jsonEntity   = JObject.FromObject(entity);
            var     customFields = JsonHelper.GetTokenValue <JObject>(jsonEntity, customFieldKey, true) ?? new JObject();

            var categories = type.MetaCategories ?? new List <ModelTypeMetaCategory>();

            foreach (var category in categories)
            {
                var attributes = new JObject();

                // Nur wenn die Sektion (category) Felder hat und wir mindestens ein öffentliches Feld haben, oder der Benutzer ein BAR Benutzer ist, gehen wir überhaupt weiter.
                // (Fall abfangen, dass eine Kategorie nur interne Felder hat
                if (category?.Fields != null && (category.Fields.Any(f => f.Visibility == (int)DataElementVisibility.@public) ||
                                                 access.RolePublicClient == AccessRoles.RoleBAR))
                {
                    foreach (var field in category.Fields)
                    {
                        // Interne Felder sind nur für BAR Benutzer sichtbar
                        if (field.Visibility == (int)DataElementVisibility.@internal && access.RolePublicClient != AccessRoles.RoleBAR)
                        {
                            continue;
                        }

                        JToken token = null;
                        var    name  = field.Name.ToLowerCamelCase();
                        if (name.StartsWith(customFieldPrefix, StringComparison.OrdinalIgnoreCase))
                        {
                            var subKey  = field.Key.Substring(customFieldPrefix.Length);
                            var subName = name.Substring(customFieldPrefix.Length);
                            name  = subName.ToLowerCamelCase();
                            token = customFields.GetTokenByKey(subName, true) ?? customFields.GetTokenByKey(subKey, true);
                        }
                        else
                        {
                            token = jsonEntity.GetTokenByKey(name, true) ?? jsonEntity.GetTokenByKey(field.Key, true);
                        }

                        if (token != null)
                        {
                            var value = field.AsFieldValue(token, language);
                            if (value != null)
                            {
                                attributes.Add(name, value);
                            }
                        }
                    }
                }

                if (attributes.Children().Any())
                {
                    metadata = metadata ?? new JObject();

                    if (category.Labels != null && category.Labels.Any())
                    {
                        var title = category.Labels.ContainsKey(language) ? category.Labels[language] : category.Labels.Values.First();
                        JsonHelper.AddOrSet(attributes, "_title", title);
                    }

                    metadata.Add(category.Identifier.ToLowerCamelCase(), attributes);
                }
            }

            return(metadata);
        }