65.9K
CodeProject 正在变化。 阅读更多。
Home

使用泛型和委托的 JSON 和 C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (2投票s)

2009 年 4 月 1 日

CPOL

3分钟阅读

viewsIcon

29434

使用泛型和委托的 JSON 和 C#

JSON,或 JavaScript 对象表示法,是一种用于传输数据的方法,类似于 XML 和其他格式。 使用此方法有很多优点。 它是人类可读的,并且可以轻松地转换为浏览器客户端的对象。

使用 JSON,我可以这样声明一个数组

var myArray = ["this","that","the other"]; 

对象表示法甚至更简单。 想象一个 C# "Person" 类

public class Person 
{
   public int ID { get; set; } 
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string SSN { get; set; }
}

如果我想将此数据传递给客户端,我可以这样做

var person = { 
   "ID" : "1", 
   "FirstName" : "Jeremy",
   "LastName" : "Likness",
   "SSN" : "111-222-3333" 
};
alert("My name is " + person.FirstName + " " + person.LastName); 

WebForms 视图引擎(你们大多数人在 .NET 中使用的引擎......虽然像 NVelocity 这样的其他视图引擎已经作为 MonoRail 项目的一部分存在,但 Microsoft 最近才发布 MVC 候选版本,该版本引入了 Model-View-Controller 视图引擎)的一个缺点是,它在呈现简单标签时会给客户端带来过重的负担。 如果你比较过 <input type="text"> <asp:TextBox>,你就会明白我的意思。

此外,开发人员往往沉迷于 AJAX 框架的“便捷性”,而常常忽略了在更新面板中包装级联下拉列表的性能影响。 一种更简单的方法是序列化这些值,并使用像 JQuery 这样的第三方工具绑定它们。

在开发此框架时,我意识到将我的领域对象序列化为 JSON 非常简单。 我想要一些通用的东西,它不依赖于反射进行抽象,并且让我可以完全控制要呈现哪些属性,而无需用属性或其他声明来污染我的领域模型,因为这些东西完全与 UI 相关。

结果呢? 一个具有 Serialize() 方法的 "JSONObject",它发出 JSON 代码。 这是该类

namespace MyJSON 
{
    /// <summary>
    ///     Delegate to determine how a value is pulled from the object
    /// </summary>
    /// <param name="instance">The instance to pull the property from</param>
    /// <param name="property">The value of the instance</param>
    /// <returns>The string representation of the value for that property</returns>
    public delegate string PropertyValue<t>(T instance, string property);

    /// <summary>
    ///     Class to help serialize AirWatch objects to the client
    /// </summary>
    public class JSONObject<T> 
    {        
        /// <summary>
        ///     The object to serialize
        /// </summary>
        private readonly T _object;

        /// <summary>
        ///     A list of properties to serialize
        /// </summary>
        private readonly List<string> _properties;

        /// <summary>
        ///     Reference to delegate to parse the value
        /// </summary>
        private readonly PropertyValue<t> _propValue; 
               
        /// <summary>
        ///     Constructor for JSON object
        /// </summary>
        /// <param name="instance">The entity instance</param>
        /// <param name="properties">The list of properties to serialize</param>
        /// <param name="propValue">The method to extract the property value</param>
        public JSONObject(T instance, IEnumerable<string> properties, 
		PropertyValue<T> propValue)
        {
            _object = instance;
            _properties = new List<string>(properties);
            _propValue = propValue; 
        }

        /// <summary>
        ///     Serialize to the JSON representation
        /// </summary>
        /// <returns>The JSON representation</returns>
        public string Serialize()
        {
            StringBuilder json = new StringBuilder();

            json.Append("{");

            bool first = true;

            foreach(string prop in _properties)
            {
                if (first)
                {
                    first = false;
                }
                else
                {
                    json.Append(","); 
                }
                string value = _propValue(_object, prop);
                json.Append(string.Format("\"{0}\":{1}", prop, EncodeJsString(value))); 
            }

            json.Append("}"); 

            return json.ToString(); 
        }
   }
}

JSON 对象接受 T 类型,可以是任何类型。 构造函数接收 "T",以及一个数组和一个委托。 该数组是一个 string 列表,描述了我感兴趣的属性。 例如,我可能只想发送 id 和姓氏,因为我不在客户端脚本中使用其他属性。 最后,是 delegate。 签名很简单:给定属性和实体,string 的值是什么? 这允许我注入逻辑以将 UI 属性映射到域属性。 发送数据的最简单方法是将它实现为一个 string,然后在客户端上适当地操作它。 毕竟,JavaScript 本身并不知道我们的任何复杂对象。

最简单的用法就是简单地发送我的一个 Person 对象并显示该名称。 例如

<b>ID: </b> <span id="personId"> </span>
<b>Name: </b> <span id="personName"> </span>

在我自己的脚本中,我只需创建我的 JSON 对象并将其序列化输出

JSONObject<Person> jsonPerson = new JSONObject<Person>(
   new Person { ID = 1, LastName="Likness" },
   new[] { "id", "name" }, 
   (entity,property) => property.Equals("id") ? entity.ID : entity.LastName
);
Page.ClientScript.RegisterClientScriptBlock(GetType(),GetType(),
   string.Format("var person={0}",jsonPerson.Serialize()),true);

最后,我需要一些东西来为我连接它,我将选择 JQuery ...

<script type="text/javascript">
   $(document).ready(function(){
      $("#personId").html(person.id);
      $("#personName").html(person.name); 
   });
</script>

script 块接受一个 type(我只是使用我所在的页面或控件的 type)、一个 key(每个 script 块应该是唯一的... 我再次在此处使用 type,但可以将其强类型化或设置为 const 等)、要发出的 JavaScript(来自 JSON 对象上的 serialize 方法),然后 true 告诉它包装我的 script 标签,因为我自己没有这样做。

这些对象可以作为回调的结果返回(只需将它们分配给 window.var 对象,以便它们可以全局访问并使用 eval)用于动态绑定,或者您可能只需渲染一个对象数组,然后执行类似的操作

for (var x = 0; x < window.json_options.length; x++) {
            var option = window.json_options[x];
            $("#selBox").append("<option value=\"" +
                option.id + "\"" + +(first ? " checked=\"checked\"" : "") 
			+ ">" + option.value + "</option>");
        }

这将绑定下拉列表。 如果你正在与“hack”的冲动作斗争,因为我们正在连接 HTML ... 请记住,这正是 ASP.NET 在幕后为你所做的事情。 最后,控件是复杂的文本渲染引擎,它们发出浏览器可以管理的片段。

当然,下一步是创建一个 JSONList<T>,我故意省略了编码脚本。 你可以在 Google 上搜索一些解决方案。

Jeremy Likness

© . All rights reserved.