c# 是否有序列化为 UrlEncoded 的方法?

c# is there a method to serialize to UrlEncoded?

我想使用 facebook 的 API,但我发现很难将对象转换为 urlEncoded。 所以,现在我有类似的东西:

string postData = JsonConvert.SerializeObject(req);

            postData = postData.Replace(@"\", "");
            postData = HttpUtility.UrlEncode(postData);

            byte[] data = Encoding.UTF8.GetBytes(postData);

            string facebookUrl = "https://graph.facebook.com/v2.5/";

问题是 facebook 不接受 jsons 但似乎是 UrlEncoded 数据,如果我错了请纠正我。

所以,我很确定将对象转换为 UrlEncoded 字符串在 .Net 4.5.1 中是不可能的,因为我已经尝试使用这些问题的一些答案,而这些答案在不久前对我不起作用。

例如:

var result = new List<string>();
            foreach (var property in TypeDescriptor.GetProperties(req))
            {
                result.Add(property.Name + "=" + property.GetValue(req));
            }

            postData = string.Join("&", result);

但是 .Name.GetValue 根本没有定义。

想得到一些帮助,TIA。

我使用的对象:

internal sealed class FacebookValidationRequest
{
    public string access_token;
    public fbReq[] batch;
    public string method;
    public string format;
    public int pretty;
    public int suppress_http_code;
    public string debug;

    public FacebookValidationRequest(string appId, string userToken)
    {
        access_token = userToken;
        batch = new[]
        {
            //test code
            new fbReq("GET", "me"),
            new fbReq("GET", "me/friends?limit=50") //,
            //new fbReq("GET", "app?access_token=" + userToken)
        };
        method = "post";
        format = "json";
        pretty = 0;
        suppress_http_code = 1;
        debug = "all";
    }

}

internal sealed class fbReq
{
    public string method;
    public string relative_url;

    public fbReq(string m, string url)
    {
        method = m;
        relative_url = url;
    }
}

FacebookValidationRequest req = new FacebookValidationRequest(appToken, userToken);

另外,拿了 facebook debugger site

的代币

facebook 希望对象在编码后的样子:

access_token=mytoken&batch=%5B%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%22%7D%2C%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%2Ffriends%3Flimit%3D50%22%7D%5D&debug=all&fields=id%2Cname&format=json&method=post&pretty=0&suppress_http_code=1

在我看来,最简单的方法是使用属性来描述您的属性,就像 .Net Json 的 DataContract 系统所做的那样。基本上,您为每个要序列化的 属性 分配一个属性,并使该属性包含将其序列化为的名称。不过,我认为您不想陷入实际编写自己的 DataContractSerializer 的混乱之中,因此简单地创建自己的 属性 class 和使用反射的简单序列化程序可能更容易。

属性class:

[AttributeUsage(AttributeTargets.Property)]
public sealed class UrlEncodeAttribute : System.Attribute
{
    public String Name { get; private set; }

    public UrlEncodeAttribute(String name)
    {
        this.Name = name;
    }
}

然后,应用于您的数据class...将属性放在所有属性上:

internal sealed class FacebookValidationRequest
{
    [UrlEncodeAttribute("access_token")]
    public String AccessToken { get; set; }
    [UrlEncodeAttribute("method")]
    public String Method { get; set; }
    [UrlEncodeAttribute("format")]
    public String Format { get; set; }
    [UrlEncodeAttribute("pretty")]
    public Int32 Pretty { get; set; }
    [UrlEncodeAttribute("suppress_http_code")]
    public Int32 SuppressHttpCode { get; set; }
    [UrlEncodeAttribute("debug")]
    public string Debug { get; set; }

    public fbReq[] Batch { get; set; }

    [UrlEncodeAttribute("batch")]
    public String BatchString
    {
        get
        {
            // put your json serialization code here to return
            // the contents of Batch as json string.
        }
    }
}

如您所见,Batch 没有 UrlEncodeAttribute,而它的字符串表示 BatchString 。它的 get 是序列化程序将调用的内容,因此您可以将转换代码放在那里。

另请注意,由于您在属性中提供的文本名称,您的属性不需要具有您在序列化中实际获得的名称,这看起来 很多 更清晰在我看来。 C# 自己对 xml 和 json 的序列化以相同的方式工作。

现在,让我们看一下实际的序列化,使用反射来获取那些属性:

public static String Serialize(Object obj, Boolean includeEmpty)
{
    // go over the properties, see which ones have a UrlEncodeAttribute, and process them.
    StringBuilder sb = new StringBuilder();
    PropertyInfo[] properties = obj.GetType().GetProperties();
    foreach (PropertyInfo p in properties)
    {
        object[] attrs = p.GetCustomAttributes(true);
        foreach (Object attr in attrs)
        {
            UrlEncodeAttribute fldAttr = attr as UrlEncodeAttribute;
            if (attr == null)
                continue;
            String objectName = fldAttr.Name;
            Object objectDataObj = p.GetValue(obj, null);
            String objectData = objectDataObj == null ? String.Empty : objectDataObj.ToString();
            if (objectData.Length > 0 || includeEmpty)
            {
                objectData = HttpUtility.UrlEncode(objectData);
                objectName= HttpUtility.UrlEncode(objectName);
                if (sb.Length > 0)
                    sb.Append("&");
                sb.Append(objectName).Append("=").Append(objectData);
            }
            break; // Only handle one UrlEncodeAttribute per property.
        }
    }
    return sb.ToString();
}

更高级的版本可以通过在 UrlEncodeAttribute class 中包含序列化方法 属性 来实现(最好使用枚举来完成),因此您可以简单地指定序列化数组即时使用 json。您显然需要将实际的 json 转换器放入 Serialize 函数中。我认为在虚拟 属性 上使用 getter 作为准备方法更简单,在这里。

显然,调用它很简单:(假设这里的 Serialize() 函数在一个名为 UrlEncodeSerializer 的 class 中)

FacebookValidationRequest fbreq = new FacebookValidationRequest();
// fill your data into fbreq here
// ...

// includeEmpty is set to true for testing here, but normally in
// UrlEncoded any missing property is just seen as empty anyway, so
// there should be no real difference.
String serialized = UrlEncodeSerializer.Serialize(fbreq, true);