自引用导航属性和json序列化

时间:2013-02-08 16:30:41

标签: c# json entity-framework asp.net-web-api navigation-properties

我有一个公开用户的Web API。

每个用户都有一位经理,他也是一位用户。

在我的EDMX中,我在“用户”实体上有一个自我1 .. *导航属性,它是从管理员到用户导航的“协作者”,用于从用户导航到用户的“管理员”管理器。

我的JSON API使用NewtonJSON来序列化实体。

为了自定义API调用的JSON结果,我在查询字符串中实现了关键字“fields”。使用“fields”可以获得用户的部分JSON表示。

呼叫如下所示:/ api / users?fields = id,name,department,picture

你从一个完整的C#用户对象中得到一个只有id,name,department和picture属性的JSON。

我使用IContractResolver的自定义实现实现了这一目标。

问题是NewtonJSON合约解析器“按类型”而不是“按对象”工作,这意味着您可以告诉序列化程序序列化声明类型的此类成员而不是另一个,但您无法分辨它(据我所知,这就是为什么我在这里要求)序列化此类型的此对象的此类成员,而不是同一类型的另一个对象上的相同成员。

那说,我的问题是当我要求:/ api / users?fields = id,name,manager

我得到一个响应,在每个用户对象上对管理员成员进行了强制序列化,如下所示:

[{
    id: 123,
    name: "Foo",
    manager:
    {
        id: 124,
        name: "Foo",
        manager:
        {
            id: 125,
            name: "Foo",
            manager:
            {
            ...
            }
        }
    }
},
{
    id: 124,
    name: "Foo",
    manager:
    {
        id: 125,
        name: "Foo",
        manager:
        {
        ...
        }
    }
},
{
    id: 125,
    name: "Foo",
    manager:
    {
    ...
    }
}]

我还实现了要求部分子实体响应的能力,如下所示:

/api/users?fields=id,name,manager.id

但由于主要对象(此处为用户)和子对象(管理器)属于同一类型,因此无法正常工作。

是否有人已经使用NewtonJSON来实现部分响应Web API?你如何设法使用嵌入式自我类型实体?

感谢您的建议。

1 个答案:

答案 0 :(得分:0)

您可以编写一个理解fields参数的动作过滤器并转换响应。我有一个示例实现,将响应对象转换为Dictionary。键是客户端询问的字段,值是响应中返回的对象的字段值。该实现是一个示例,并没有接近完成:)

public class FieldFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var fieldsParameter = actionExecutedContext.Request.GetQueryNameValuePairs().Where(kvp => kvp.Key == "fields");
        if (fieldsParameter.Count() == 1)
        {
            object response;
            object newResponse;

            if (actionExecutedContext.Response.TryGetContentValue(out response))
            {
                string[] fields = fieldsParameter.First().Value.Split(',');

                IEnumerable<object> collection = response as IEnumerable<object>;
                if (collection != null)
                {
                    newResponse = collection.Select(o => SelectFields(fields, o));
                }
                else
                {
                    newResponse = SelectFields(fields, response);
                }

                actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.OK, newResponse);
            }
        }
    }

    private static Dictionary<string, object> SelectFields(string[] fields, object value)
    {
        Dictionary<string, object> result = new Dictionary<string, object>();

        foreach (string field in fields)
        {
            result.Add(field, GetValue(field, value));
        }

        return result;
    }

    private static object GetValue(string indexer, object value)
    {
        string[] indexers = indexer.Split('.');

        foreach (string property in indexers)
        {
            PropertyInfo propertyInfo = value.GetType().GetProperty(property);
            if (propertyInfo == null)
            {
                throw new Exception(String.Format("property '{0}' not found on type '{1}'", property, value.GetType()));
            }

            value = propertyInfo.GetValue(value);

            if (value == null)
            {
                return null;
            }
        }

        return value;
    }
}