'소프트웨어'에 해당되는 글 284건

  1. 2010.02.18 Xml to Json
  2. 2010.02.10 JQGrid using MVC, Json and Datatable.
  3. 2010.01.13 uki - ui
  4. 2010.01.12 Best jQuery plugins - September 2009
  5. 2010.01.12 C# 강좌
  6. 2010.01.12 엔코아와 함께하는 DB 최적화 여행 - 실행계획
  7. 2010.01.05 기업에서도 무료로 사용할 수 있는 백신, Comodo Internet Security
  8. 2009.12.29 IP works but not Named Pipes, Connecting to SQL 2005 (SQL Auth)
  9. 2009.12.18 Prototype - Element class methods
  10. 2009.12.15 C# 4.0의 새로운 기능
2010.02.18 10:48

Xml to Json

How to convert XML to JSON in ASP.NET C#


Last modified: 18 July 2006.   Any comments or suggestions - please fill in form below. Chris Cant.
No download provided - cut and paste to try out. The following code is provided as-is without any warranty of any kind.
  • Code to convert XML to JSON in ASP.NET C#
  • Example of how to insert JSON code into a page when the page is generated
  • Please acknowledge PHD Computer Consultants Ltd (PHDCC) if you use this code
  • Introduction

    JSON is a lightweight data-interchange format. It is particularly useful because it can be 'decoded' easily by web page JavaScript into object form.

    AJAX-based web pages use XmlHttpRequest to receive data from a server in response to a user action. While the returned data is normally in XML format, it can also be returned in JSON string format and processed more easily in JavaScript.

    Many applications may store information in XML format. However they may want to send data to a client using JSON. To achieve this, they must convert their XML data into JSON format. The ASP.NET C# code below does this job.

    Code Description

    The code provides a method private static string XmlToJSON(XmlDocument xmlDoc) that converts an XmlDocument into a JSON string. The code iterates through each XML element, its attributes and children, creating the corresponding JSON objects.
    • The code never generates number or boolean values.
    • The XML documentElement is always reported as member:object even if it could be shortened by the following rules.
    • Element attributes are converted into member "attr_name":"attr_value".
      XML JSON
      <xx yy='nn'></xx> { "xx": {"yy":"nn"} }
      <xx yy=''></xx> { "xx": {"yy":""} }
    • Element children with no attributes, children or text are converted into member "child_name":null
      XML JSON
      <xx/> { "xx":null }
    • Element children with no attributes or children but contain text are converted into "child_name":"child_text"
      XML JSON
      <xx>yyy</xx> { "xx":"yyy" }
    • Other element attributes and children are converted into "child_name":object or an array "child_name":[elements] as appropriate, with element text converted into a member with name "value"
      XML JSON
      <xx yy='nn'><mm>zzz</mm></xx> { "xx": {"yy":"nn", "mm":"zzz"} }
      <xx yy='nn'><mm>zzz</mm><mm>aaa</mm></xx> { "xx": {"yy":"nn", "mm": [ "zzz", "aaa" ] } }
      <xx><mm>zzz</mm>some text</xx> { "xx": {"mm":"zzz", "value":"some text"} }
      <xx value='yyy'>some text<mm>zzz</mm>more text</xx> { "xx": {"mm":"zzz", "value": [ "yyy", "some text", "more text" ] } }
    • Characters are made safe for conversion into JSON. Note that this does not protect your JavaScript from attack if any of the source XML comes from an unsafe source, eg user input.
      XML JSON
      <aa>/z'z"z\yyy<aa>< {"aa": "\/z\u0027z\"z\\yyy" }

    In some special circumstances, such as in the example below, you may need to escape the backslash characters again, eg:

    string JSON = XmlToJSON(doc);
    JSON = JSON.Replace(@"\", @"\\");

    Note that there may be security implications for web pages using unchecked XML contents.

    Example

    The examples on this page come from my Space Browse site.

    XML Input:

    <space name="Cake Collage">
    <frame>
      <photo img="cakecollage1.jpg" />
      <text string="Browse my cake space" />
      <rule type="F" img="cakecollage9.jpg" x="150" y="0" w="300" h="250" />
      <rule type="F" img="cakecollage2.jpg" x="0" y="0" w="150" h="220" />
    </frame>
    <frame>
      <photo img="cakecollage2.jpg" />
      <rule type="B" img="cakecollage1.jpg" />
      <rule type="L" img="cakecollage3.jpg" />
    </frame>
    </space>
    

    JSON Output (re-formatted):

    { "space":
      { "name": "Cake Collage",
        "frame": [ {"photo": { "img": "cakecollage1.jpg" },
                    "rule": [ { "type": "F",
                                "img": "cakecollage9.jpg",
                                "x": "150",
                                "y": "0",
                                "w": "300",
                                "h": "250"
                              }, 
                              { "type": "F",
                                "img": "cakecollage2.jpg",
                                "x": "0",  
                                "y": "0",  
                                "w": "150",  
                                "h": "220" 
                              }
                            ],
                    "text": { "string": "Browse my cake space" }
                   },
                   {"photo": { "img": "cakecollage2.jpg" },
                    "rule": [ { "type": "B", "img": "cakecollage1.jpg" },
                              { "type": "L",  "img": "cakecollage3.jpg" }
                            ]
                   }
                 ]
      }
    }
    

    Once the JSON has been converted into a JavaScript object, eg called space_DOM, the following objects are available:

    • space_DOM.space.name
    • space_DOM.space.frame.length
    • space_DOM.space.frame[0].text.string
    • space_DOM.space.frame[0].rule[0].type

    Your JavaScript code should be flexible to cope with members not existing, members existing as a single value, or members existing as an array. I find it useful to have a JavaScript function ObjectToArray which converts all these cases into an Array of length 0, 1 or greater.

    function ObjectToArray( obj)
    {
        if( !obj) return new Array();
        if( !obj.length) return new Array(obj);
        return obj;
    }
    
    space_DOM.space.frame = ObjectToArray(space_DOM.space.frame);
    

    XmlToJSON C# code

    You may wish to use some of the updates suggsted in the comments below.
    private static string XmlToJSON(XmlDocument xmlDoc)
    {
        StringBuilder sbJSON = new StringBuilder();
        sbJSON.Append("{ ");
        XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true);
        sbJSON.Append("}");
        return sbJSON.ToString();
    }
    
    //  XmlToJSONnode:  Output an XmlElement, possibly as part of a higher array
    private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
    {
        if (showNodeName)
            sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
        sbJSON.Append("{");
        // Build a sorted list of key-value pairs
        //  where   key is case-sensitive nodeName
        //          value is an ArrayList of string or XmlElement
        //  so that we know whether the nodeName is an array or not.
        SortedList childNodeNames = new SortedList();
    
        //  Add in all node attributes
        if( node.Attributes!=null)
            foreach (XmlAttribute attr in node.Attributes)
                StoreChildNode(childNodeNames,attr.Name,attr.InnerText);
    
        //  Add in all nodes
        foreach (XmlNode cnode in node.ChildNodes)
        {
            if (cnode is XmlText)
                StoreChildNode(childNodeNames, "value", cnode.InnerText);
            else if (cnode is XmlElement)
                StoreChildNode(childNodeNames, cnode.Name, cnode);
        }
    
        // Now output all stored info
        foreach (string childname in childNodeNames.Keys)
        {
            ArrayList alChild = (ArrayList)childNodeNames[childname];
            if (alChild.Count == 1)
                OutputNode(childname, alChild[0], sbJSON, true);
            else
            {
                sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                foreach (object Child in alChild)
                    OutputNode(childname, Child, sbJSON, false);
                sbJSON.Remove(sbJSON.Length - 2, 2);
                sbJSON.Append(" ], ");
            }
        }
        sbJSON.Remove(sbJSON.Length - 2, 2);
        sbJSON.Append(" }");
    }
    
    //  StoreChildNode: Store data associated with each nodeName
    //                  so that we know whether the nodeName is an array or not.
    private static void StoreChildNode(SortedList childNodeNames, string nodeName, object nodeValue)
    {
    	// Pre-process contraction of XmlElement-s
        if (nodeValue is XmlElement)
        {
            // Convert  <aa></aa> into "aa":null
            //          <aa>xx</aa> into "aa":"xx"
            XmlNode cnode = (XmlNode)nodeValue;
            if( cnode.Attributes.Count == 0)
            {
                XmlNodeList children = cnode.ChildNodes;
                if( children.Count==0)
                    nodeValue = null;
                else if (children.Count == 1 && (children[0] is XmlText))
                    nodeValue = ((XmlText)(children[0])).InnerText;
            }
        }
        // Add nodeValue to ArrayList associated with each nodeName
        // If nodeName doesn't exist then add it
        object oValuesAL = childNodeNames[nodeName];
        ArrayList ValuesAL;
        if (oValuesAL == null)
        {
            ValuesAL = new ArrayList();
            childNodeNames[nodeName] = ValuesAL;
        }
        else
            ValuesAL = (ArrayList)oValuesAL;
        ValuesAL.Add(nodeValue);
    }
    
    private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName)
    {
        if (alChild == null)
        {
            if (showNodeName)
                sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
            sbJSON.Append("null");
        }
        else if (alChild is string)
        {
            if (showNodeName)
                sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
            string sChild = (string)alChild;
            sChild = sChild.Trim();
            sbJSON.Append("\"" + SafeJSON(sChild) + "\"");
        }
        else
            XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName);
        sbJSON.Append(", ");
    }
    
    // Make a string safe for JSON
    private static string SafeJSON(string sIn)
    {
        StringBuilder sbOut = new StringBuilder(sIn.Length);
        foreach (char ch in sIn)
        {
            if (Char.IsControl(ch) || ch == '\'')
            {
                int ich = (int)ch;
                sbOut.Append(@"\u" + ich.ToString("x4"));
                continue;
            }
            else if (ch == '\"' || ch == '\\' || ch == '/')
            {
                sbOut.Append('\\');
            }
            sbOut.Append(ch);
        }
        return sbOut.ToString();
    }
    

    Using XmlToJSON

    The following code shows how to use XmlToJSON() when an ASP.NET 2 page loads. It then uses the ASP.NET2 ClientScriptManager to insert code containing the JSON string into the web page. See the following section for an example of JavaScript space_processJSON().

    protected void Page_Load(object sender, EventArgs e)
    {
        XmlDocument doc = new XmlDocument();
        try
        {
            string path = Server.MapPath(".");
            doc.Load(path+"whatever.xml");
        }
        catch (Exception ex)
        {
            lblError.Text = ex.ToString();
            return;
        }
    
        // Convert XML to a JSON string
        string JSON = XmlToJSON(doc);
        
        // Replace \ with \\ because string is being decoded twice
        JSON = JSON.Replace(@"\", @"\\");
        
        // Insert code to process JSON at end of page
        ClientScriptManager cs = Page.ClientScript;
        cs.RegisterStartupScript(GetType(), "SpaceJSON", "space_processJSON('" + JSON + "');", true);
    }
    

    Client-side code

    <script src="space/json.js" type="text/javascript"></script>
    
    <script type="text/javascript">
    function space_processJSON( JSON)
    {
        space_DOM = JSON.parseJSON();
        if( !space_DOM)
        {
            alert("JSON decode error");
            return;
        }
        space_DOM.space.frame = ObjectToArray(space_DOM.space.frame);
        space_frameCount = space_DOM.space.frame.length;
        .. or whatever
    }
    </script>
    

    Comments:

    Michael, Mon, 19 Jun 2006 16:46:05 (GMT)
    See this implementation: http://groups.google.de/group/ajaxpro/browse_thread/thread/219f830011e5ca6f/9e72c85fcf802a84#9e72c85fcf802a84

    Damon Carr, Thu, 07 Sep 2006 12:24:31 (GMT)
    Excellent work.

    Alex Egg, Sun, 31 Dec 2006 06:31:43 (GMT)
    Question: Why is XmlToJSON private? Wouldn't it be more appropriate for this method to be declared as public? Also, I think you should change the XmlDocument parameter of XmlToJSON to an XmlNode, it would be much more versatile.
    Also, I have discovered you code does not produce correct JSON when the xml contains cdata blocks

    Eric Walker, Mon, 8 Oct 2007 08:01:45 -0700
    I found that an empty xml node was not being decoded properly (an extra } was being added). So for example <foo /> would be translated as {"foo" : }}
    By tracking whether or not a child was added, I was able to work arround this issue:
    //  XmlToJSONnode:  Output an XmlElement, possibly as part of a higher array
            public static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
            {
                bool childAdded = false;
                if (showNodeName)
                    sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
                sbJSON.Append("{");
                // Build a sorted list of key-value pairs
                //  where   key is case-sensitive nodeName
                //          value is an ArrayList of string or XmlElement
                //  so that we know whether the nodeName is an array or not.
                SortedList childNodeNames = new SortedList();
    
                //  Add in all node attributes
                if (node.Attributes != null)
                    foreach (XmlAttribute attr in node.Attributes)
                        StoreChildNode(childNodeNames, attr.Name, attr.InnerText);
    
                //  Add in all nodes
                foreach (XmlNode cnode in node.ChildNodes)
                {
                    childAdded = true;
                    if (cnode is XmlText)
                        StoreChildNode(childNodeNames, "value", cnode.InnerText);
                    else if (cnode is XmlElement)
                        StoreChildNode(childNodeNames, cnode.Name, cnode);
                }
    
                // Now output all stored info
                foreach (string childname in childNodeNames.Keys)
                {
                    childAdded = true;
                    ArrayList alChild = (ArrayList)childNodeNames[childname];
                    if (alChild.Count == 1)
                        OutputNode(childname, alChild[0], sbJSON, true);
                    else
                    {
                        sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                        foreach (object Child in alChild)
                            OutputNode(childname, Child, sbJSON, false);
                        sbJSON.Remove(sbJSON.Length - 2, 2);
                        sbJSON.Append(" ], ");
                    }
                }
                sbJSON.Remove(sbJSON.Length - 2, 2);
                if (childAdded)
                {
                    sbJSON.Append(" }");
                }
                else
                {
                    sbJSON.Append(" null");
                }
            }
    
    I hope this is helpful.

    Leon, Mon, 22 Oct 2007 09:20:55 -0700
    Another way to do it (DataContractJsonSerializer):
    http://blogs.msdn.com/kaevans/archive/2007/09/04/use-linq-and-net-3-5-to-convert-rss-to-json.aspx

    Mark Brito, Tue, 19 Feb 2008 21:16:32 GMT
    In spirit of helping .. I found a bug where there is only one element in the xml, it would make it a single element instead of an array.. simply change the following line of code..
    if (alChild.Count == 1)
    to
    if (alChild.Count == 1 && (alChild[0] is string))
    Below is the entire function.
    public static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
    {
        bool childAdded = false;
        if (showNodeName)
            sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
        sbJSON.Append("{");
        // Build a sorted list of key-value pairs
        //  where   key is case-sensitive nodeName
        //          value is an ArrayList of string or XmlElement
        //  so that we know whether the nodeName is an array or not.
        SortedList childNodeNames = new SortedList();
    
        //  Add in all node attributes
        if (node.Attributes != null)
            foreach (XmlAttribute attr in node.Attributes)
                StoreChildNode(childNodeNames, attr.Name, attr.InnerText);
    
        //  Add in all nodes
        foreach (XmlNode cnode in node.ChildNodes)
        {
            childAdded = true;
            if (cnode is XmlText)
                StoreChildNode(childNodeNames, "value", cnode.InnerText);
            else if (cnode is XmlElement)
                StoreChildNode(childNodeNames, cnode.Name, cnode);
        }
    
        // Now output all stored info
        foreach (string childname in childNodeNames.Keys)
        {
            childAdded = true;
            ArrayList alChild = (ArrayList)childNodeNames[childname];
            bool bFlag = false;
            foreach (object oChild in alChild) bFlag = true;
            if (alChild.Count == 1 && (alChild[0] is string))
                OutputNode(childname, alChild[0], sbJSON, true);
            else
            {
                sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                foreach (object Child in alChild)
                    OutputNode(childname, Child, sbJSON, false);
                sbJSON.Remove(sbJSON.Length - 2, 2);
                sbJSON.Append(" ], ");
            }
        }
        sbJSON.Remove(sbJSON.Length - 2, 2);
        if (childAdded)
        {
            sbJSON.Append(" }");
        }
        else
        {
            sbJSON.Append(" null");
        }
    }

    Milind Amin, Fri, 25 Jul 2008 06:52:56 GMT
    Thanks. Very Good Article.

    Paul Chu, Thu, 16 Oct 2008 01:52:44 GMT
    Thank you and all the other contributors for this excellent article.
    Does the last post contain all the suggested enhancements ?

    Answer: I haven't tested the suggestions but they look good!

    Chris, Thu, 13 Nov 2008 04:47:25 GMT
    I could see where this would come in handy. Hats off to you for that. Overall, I would prefer to build JSON from real objects which gives me the ability to serialize to XML, JSON, or whatever.

    noone, Mon, 06 Apr 2009 12:55:55 GMT
    Use the .NET 3.5 JavaScript Serializer: System.Web.Script.Serialization.JavaScriptSerializer

    Michele Costabile, Wed, 20 May 2009 13:33:31 GMT
    I had a problem with an extra brace at the end of the file. I solved it checking that I really had to remove two characters from the end of the string buffer in XmlToJSONnode. The following is my version of the function. Maybe further inspection of the code would be in order for finding a more elegant solution, but this is what I managed to do in a short time.
    //  XmlToJSONnode:  Output an XmlElement, possibly as part of a higher array
    private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
    {
        if (showNodeName)
            sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
        sbJSON.Append("{");
        // Build a sorted list of key-value pairs
        //  where   key is case-sensitive nodeName
        //          value is an ArrayList of string or XmlElement
        //  so that we know whether the nodeName is an array or not.
        SortedList childNodeNames = new SortedList();
    
        //  Add in all node attributes
        if (node.Attributes != null)
            foreach (XmlAttribute attr in node.Attributes)
                StoreChildNode(childNodeNames, attr.Name, attr.InnerText);
    
        //  Add in all nodes
        foreach (XmlNode cnode in node.ChildNodes)
        {
            if (cnode is XmlText)
                StoreChildNode(childNodeNames, "value", cnode.InnerText);
            else if (cnode is XmlElement)
                StoreChildNode(childNodeNames, cnode.Name, cnode);
        }
    
        // Now output all stored info
        bool hasAddedChild = false;
        foreach (string childname in childNodeNames.Keys)
        {
            ArrayList alChild = (ArrayList)childNodeNames[childname];
            if (alChild.Count == 1 && (alChild[0] is string))
            {
                hasAddedChild = true;
                OutputNode(childname, alChild[0], sbJSON, true);
            }
            else
            {
                sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                foreach (object Child in alChild)
                {
                    hasAddedChild = true;
                    OutputNode(childname, Child, sbJSON, false);
                }
                if (hasAddedChild)
                    sbJSON.Remove(sbJSON.Length - 2, 2);
                sbJSON.Append(" ], ");
            }
        }
        if (hasAddedChild)
            sbJSON.Remove(sbJSON.Length - 2, 2);
        sbJSON.Append(" }");
    }
    

    Overide, Thu, 09 Jul 2009 11:09:49 GMT
    ObjectToArray(obj): function is incorrect if obj is String. Maybe better:
    function ObjectToArray( obj)
    {
        if (!obj) return new Array();
        if (!(obj instanceof Array)) return new Array(obj);
        return obj;
    }
    

    Override, Fri, 10 Jul 2009 08:39:11 GMT
    and also it conflicts with JQuery. Line: v = f(v); - not enough memory

    lucky, Fri, 21 Aug 2009 01:29:39 GMT
    Thank

    Karl, Thu, 15 Oct 2009 23:46:49 GMT
    I added a check for numeric values, to optionally display the quotes.
    In OutputNode() change the one line with the quotes to:
    Double temp;
    if (Double.TryParse(sChild, out temp))
     sbJSON.Append(SafeJSON(sChild));
    else
     sbJSON.Append("\"" + SafeJSON(sChild) + "\"");

    출처 : http://www.phdcc.com/xml2json.htm

    '소프트웨어 > C# & ASP.NET' 카테고리의 다른 글

    Xml to Json  (0) 2010.02.18
    JQGrid using MVC, Json and Datatable.  (0) 2010.02.10
    C# 강좌  (0) 2010.01.12
    C# 4.0의 새로운 기능  (0) 2009.12.15
    잉여들을 위한 클래스설계 이야기 2/4  (0) 2009.12.11
    잉여들을 위한 클래스설계 이야기 1/4  (0) 2009.12.11
    Trackback 3 Comment 0
    2010.02.10 15:10

    JQGrid using MVC, Json and Datatable.

    Last couple of days i have been trying to make my sample JQGrid working ASP.NET MVC and DataTable. If you google it with this two terms MVC,JQGrid you will find lot of samples using Linq, but if you work with Databases like Oracle or any other databases which does not have a LINQ provider(atleast at the time of writing this article) your alternate choice is to go with DataSet/DataTable. So i thought of putting this example together to help others who are on the same boat like myself.

    I have given a fully working sample of ASP.NET MVC with JQgrid using Datatable(See below for the download link).

    I am not going to cover the basics of MVC in this article, for which you can refer to other blogs such as this one.

    These are the features i have implemented in this sample,


    • Themes
    • Refresh Grid
    • Server side Paging
    • Sorting
    • JSON based


    I will cover other features of JQGrid in my future articles.

    Here are the steps to get started,

    1. Download JQGrid from here

    2. Create an MVC Application using the Visual Studio 2008 template( if you want a detailed explanation for creating an MVC application VS Template refer here).

    3. Now move the downloaded JQGrid files into the <project>/scripts folders.

    4. Usually with MVC application people tend to put all the themes under Content folder, if you do that here you will have to modify the js files for paging button's images.So i wouldn't recommend moving themes folder.

    4. Open the Site.Master inside <project>/Shared/Site.Master and add links to the following files,
    ../../Scripts/themes/steel/grid.css
    ../../Scripts/themes/jqModal.css
    ../../Scripts/jquery.jqGrid.js
    ../../Scripts/js/jqModal.js
    ../../Scripts/js/jqDnR.js

    5. If you don't like steel themes there 4 other themes( basic,coffee,green and sand) available inside themes folder.

    6. Now you site.master will look similar to this.

    1. <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>  
    2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  
    3. <html xmlns="http://www.w3.org/1999/xhtml">  
    4. <head runat="server">  
    5.     <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>  
    6.     <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />  
    7.     <script src="/Scripts/jquery-1.3.2.js" type="text/javascript"></script>      
    8.     <script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>  
    9.     <link rel="stylesheet" type="text/css" href="../../Scripts/themes/steel/grid.css" title="steel"  
    10.         media="screen" />  
    11.     <link href="../../Scripts/themes/jqModal.css" rel="stylesheet" type="text/css" />  
    12.     <script src="../../Scripts/jquery.jqGrid.js" type="text/javascript"></script>  
    13.     <script src="../../Scripts/js/jqModal.js" type="text/javascript"></script>  
    14.     <script src="../../Scripts/js/jqDnR.js" type="text/javascript"></script>  
    15.       
    16.     <asp:ContentPlaceHolder ID="HeadContent" runat="server" />      
    17. </head>  
    18. <body>  
    19.     <div class="page">  
    20.         <div id="header">  
    21.             <div id="title">  
    22.                 <h1>Sample from arahuman.blogspot.com</h1>  
    23.             </div>  
    24.             <div id="logindisplay">  
    25.                 <% Html.RenderPartial("LogOnUserControl"); %>  
    26.             </div>   
    27.             <div id="menucontainer">  
    28.                 <ul id="menu">                
    29.                     <li><%= Html.ActionLink("Home", "Index", "Home")%></li>  
    30.                     <li><%= Html.ActionLink("About", "About", "Home")%></li>  
    31.                 </ul>  
    32.             </div>  
    33.         </div>  
    34.         <div id="main">  
    35.             <asp:ContentPlaceHolder ID="MainContent" runat="server" />  
    36.             <div id="footer">  
    37.             </div>  
    38.         </div>  
    39.     </div>  
    40. </body>  
    41. </html>  



    7. Create a folder named Helper under the <project>/Helper folder and add the following Helper method to convert a Datatable into the JSON format.

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Web;  
    5. using System.Data;  
    6. using Newtonsoft.Json;  
    7. using System.Text;  
    8. using System.IO;  
    9.   
    10. namespace JQGridMVCDemo.Helper {  
    11.     public class JsonHelper {  
    12.         public static string JsonForJqgrid(DataTable dt, int pageSize, int totalRecords,int page) {  
    13.             int totalPages = (int)Math.Ceiling((float)totalRecords / (float)pageSize);  
    14.             StringBuilder jsonBuilder = new StringBuilder();  
    15.             jsonBuilder.Append("{");  
    16.             jsonBuilder.Append("\"total\":" + totalPages + ",\"page\":" + page + ",\"records\":" + (totalRecords) + ",\"rows\"");  
    17.             jsonBuilder.Append(":[");  
    18.             for (int i = 0; i < dt.Rows.Count; i++) {  
    19.                 jsonBuilder.Append("{\"i\":"+ (i) +",\"cell\":[");  
    20.                 for (int j = 0; j < dt.Columns.Count; j++) {  
    21.                     jsonBuilder.Append("\"");  
    22.                     jsonBuilder.Append(dt.Rows[i][j].ToString());  
    23.                     jsonBuilder.Append("\",");  
    24.                 }  
    25.                 jsonBuilder.Remove(jsonBuilder.Length - 1, 1);  
    26.                 jsonBuilder.Append("]},");  
    27.             }  
    28.             jsonBuilder.Remove(jsonBuilder.Length - 1, 1);  
    29.             jsonBuilder.Append("]");  
    30.             jsonBuilder.Append("}");  
    31.             return jsonBuilder.ToString();  
    32.         }  
    33.     }  
    34. }  



    8. Now open the index page under <project>/SViews/Home/Index.aspx and add the following code,

    1. <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>  
    2. <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">  
    3.     Home Page  
    4. </asp:Content>  
    5. <asp:Content ID="indexContent" ContentPlaceHolderID="HeadContent" runat="server">  
    6.     <script type="text/javascript">  
    7.         jQuery(document).ready(function() {  
    8.             jQuery("#list").jqGrid({  
    9.             url: '/Home/GetGridData/',  
    10.                 datatype: 'json',  
    11.                 mtype: 'GET',  
    12.                 colNames: ['Customer ID', 'Contact Name', 'Address', 'City', 'Postal Code'],  
    13.                 colModel: [  
    14.                   { name: 'CustomerID', index: 'CustomerID', width: 100, align: 'left' },  
    15.                   { name: 'ContactName', index: 'ContactName', width: 150, align: 'left' },  
    16.                   { name: 'Address', index: 'Address', width: 300, align: 'left' },  
    17.                   { name: 'City', index: 'City', width: 150, align: 'left' },  
    18.                   { name: 'PostalCode', index: 'PostalCode', width: 100, align: 'left' }  
    19.                 ],  
    20.                 pager: jQuery('#pager'),  
    21.                 rowNum: 10,  
    22.                 rowList: [5, 10, 20, 50],  
    23.                 sortname: 'CustomerID',  
    24.                 sortorder: "asc",  
    25.                 viewrecords: true,  
    26.                 imgpath: '/scripts/themes/steel/images',  
    27.                 caption: 'Northwind Customer Information'  
    28.             }).navGrid(pager, { edit: false, add: false, del: false, refresh: true, search: false });  
    29.         });  
    30.     </script>  
    31.   
    32. </asp:Content>  
    33. <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">  
    34.     <h2>  
    35.         Customers List</h2>  
    36.     <table id="list" class="scroll" cellpadding="0" cellspacing="0" width="100%">  
    37.     </table>  
    38.     <div id="pager" class="scroll" style="text-align: center;">  
    39.     </div>  
    40. </asp:Content>  



    the id (#list) links the html table with the jquery to inject the grid ui's code at runtime.
    it makes an ajax calls using the url(/Home/GetGridData/) provided.
    datatype: json refers to the output from the above call returns the JSON type results.

    9. Now open the home controller page to add the GetGridDataMethod under <project>/Controller/HomeController.cs. Add the following code to it.

    1. using System;  
    2. using System.Collections.Generic;  
    3. using System.Linq;  
    4. using System.Web;  
    5. using System.Web.Mvc;  
    6. using System.Data.SqlClient;  
    7. using System.Configuration;  
    8. using System.Data;  
    9. using JQGridMVCDemo.Helper;  
    10.   
    11. namespace MvcApplication1.Controllers {  
    12.     [HandleError]  
    13.     public class HomeController : Controller {  
    14.         public ActionResult Index() {  
    15.             ViewData["Message"] = "Welcome to ASP.NET MVC!";  
    16.             return View();  
    17.         }  
    18.   
    19.         public ActionResult About() {  
    20.             return View();  
    21.         }  
    22.   
    23.         public ActionResult GetGridData(string sidx, string sord, int page, int rows) {  
    24.             return Content(JsonHelper.JsonForJqgrid(GetDataTable(sidx,sord,page,rows), rows, GetTotalCount(), page), "application/json");  
    25.         }  
    26.   
    27.         public DataTable GetDataTable(string sidx, string sord, int page, int pageSize) {  
    28.             int startIndex = (page-1) * pageSize;  
    29.             int endIndex = page * pageSize;  
    30.             string sql = @"WITH PAGED_CUSTOMERS  AS  
    31.                         (  
    32.                          SELECT  CustomerID, ContactName, Address, City, PostalCode,  
    33.                            ROW_NUMBER() OVER (ORDER BY " + sidx + @" " + sord + @") AS RowNumber  
    34.                          FROM CUSTOMERS  
    35.                         )  
    36.                         SELECT CustomerID, ContactName, Address, City, PostalCode  
    37.                         FROM PAGED_CUSTOMERS  
    38.                         WHERE RowNumber BETWEEN " + startIndex + @" AND " + endIndex + @";";  
    39.                           
    40.             DataTable dt = new DataTable();  
    41.             SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["mainConnection"].ConnectionString);  
    42.             SqlDataAdapter adap = new SqlDataAdapter(sql,conn);  
    43.             var rows=adap.Fill(dt);  
    44.             return dt;  
    45.         }  
    46.   
    47.         public int GetTotalCount() {  
    48.             string sql = @"SELECT COUNT(*) FROM Customers";  
    49.             SqlConnection conn=null;  
    50.             try {  
    51.                  conn= new SqlConnection(ConfigurationManager.ConnectionStrings["mainConnection"].ConnectionString);  
    52.                 SqlCommand comm = new SqlCommand(sql, conn);  
    53.                 conn.Open();  
    54.                 return (int)comm.ExecuteScalar();  
    55.             } catch {  
    56.             } finally {  
    57.                 try {  
    58.                     if (ConnectionState.Closed != conn.State) {  
    59.                         conn.Close();  
    60.                     }  
    61.                 }catch {  
    62.                 }  
    63.             }  
    64.             return -1;  
    65.         }  
    66.     }  
    67. }  




    I have declared four paramters here which will be passed by the JQuery. To help us understand better i have named it same like the JGrid where sidx refers to Sort Index name, sord refers to Sort Direction, page refers to page being invoked and rows refers to rows per page.

    That's it. You can download the fully functional source code here. Enjoy and leave me a comment if you like it.

     

    출처 :ㅣ http://arahuman.blogspot.com/2009/06/jqgrid-using-mvc-json-and-datatable.html

    '소프트웨어 > C# & ASP.NET' 카테고리의 다른 글

    Xml to Json  (0) 2010.02.18
    JQGrid using MVC, Json and Datatable.  (0) 2010.02.10
    C# 강좌  (0) 2010.01.12
    C# 4.0의 새로운 기능  (0) 2009.12.15
    잉여들을 위한 클래스설계 이야기 2/4  (0) 2009.12.11
    잉여들을 위한 클래스설계 이야기 1/4  (0) 2009.12.11
    Trackback 9 Comment 0
    2010.01.13 08:59

    uki - ui

    http://ukijs.org/

    복잡한 UI도 쉽게~~ wave을 100라인으로....
    Trackback 0 Comment 0
    2010.01.12 08:41

    Best jQuery plugins - September 2009

    http://www.ajaxline.com/jquery-plugins-september-2009
    Trackback 0 Comment 0
    2010.01.12 08:35

    C# 강좌

    C# 강좌

    '소프트웨어 > C# & ASP.NET' 카테고리의 다른 글

    Xml to Json  (0) 2010.02.18
    JQGrid using MVC, Json and Datatable.  (0) 2010.02.10
    C# 강좌  (0) 2010.01.12
    C# 4.0의 새로운 기능  (0) 2009.12.15
    잉여들을 위한 클래스설계 이야기 2/4  (0) 2009.12.11
    잉여들을 위한 클래스설계 이야기 1/4  (0) 2009.12.11
    Trackback 6 Comment 0
    2010.01.12 08:33

    엔코아와 함께하는 DB 최적화 여행 - 실행계획





    출처 : http://aspdotnet.tistory.com/entry/%EC%97%94%EC%BD%94%EC%95%84%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-DB-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%97%AC%ED%96%89-%EC%8B%A4%ED%96%89%EA%B3%84%ED%9A%8D
    Trackback 2 Comment 0
    2010.01.05 23:35

    기업에서도 무료로 사용할 수 있는 백신, Comodo Internet Security

    기업에서 사용할 수 있는 소프트웨어의 라이센스는 개인의 그것과는 상당한 차이가 있습니다. 프리웨어라고 해도 개인에게만 해당하지 기업은 해당 사항이 없는 경우가 많기 때문에 그냥 무의식적으로 ‘무료’를 사용하다가 기업에 손해를, 개인에게는 불명예를 안겨주는 경우가 상당히 많았습니다.

    그래서 ‘당장 컴퓨터에서 지워야하는 상용 소프트웨어들’이라는 살짝 도발적인 제목으로 글을 하나 올렸었고, 이게 다음뷰의 트래픽 폭탄을 맞은 적도 있었습니다.

    관련 글 : http://www.heybears.com/2512624

    이 글 하단에 보면 ‘백신’ 영역에 무료 백신 소프트웨어로 CalmAV만 적어 놓았었는데, 여기에 코모도도 기업과 개인 모두에게 무료라는 댓글도 달린 바 있습니다.

    이때만 해도 그런가보다 하고 넘어 갔었는데, 파코즈에 다시 관련된 글이 올라왔네요.

    기업에서도 무료인가에 대한 질문답변 링크도 친절하게 올라와 있습니다.

    관련 글 : http://forums.comodo.com/install_setup_configuration_faq/is_cis_free_for_business_usage-t41376.0.html

    다운로드는 http://personalfirewall.comodo.com/download_firewall.html 여기서 하면 되고, 코모도 3.13 버전의 한글 패치는 http://softwant.x-y.net/phpbb3/viewtopic.php?f=2&t=1 여기서 하면 됩니다.

    한글 패치가 적용된 코모도는 아래와 같은 모습이라고 합니다.

     

    물론 정품 윈도우를 사용하면(브랜드 PC의 OEM OS 포함) 그럭저럭 Microsoft Security Essentials를 사용해도 됩니다. 현재 자신의 컴퓨터에 백신으로 알약이나 네이버 툴바에서 제공하는 것이 설치되어 있는 분들은 이참에 코모도로 갈아 타 보시면 어떨까요?

    출처 : http://www.heybears.com/2512708

    Trackback 0 Comment 0
    2009.12.29 15:51

    IP works but not Named Pipes, Connecting to SQL 2005 (SQL Auth)

    [Microsoft][SQL Native Client]Named Pipes Provider: Could not open a connection to SQL Server [1326]

    IP works but not Named Pipes, Connecting to SQL 2005 (SQL Auth)

    I have SQL 2005 Std Edition, Mixed Security, and remotely connecting by TCP/IP works great.

    Named Pipes (and the SQL Native Client) is the only issue.

     

    The problem _appears_ to be that I'm not authenticated under the SQL Machine; however, I'm not attempting to use Windows Authentication, I'm strictly using SQL, so I do not understand why I'm having a problem.

     

    My reasoning for the above conclusion, is that the command "net use \\[server_name]\ipc$" will allow me to connect to the server, and Named Pipes will begin working.  Again, remember that I'm only trying to access named pipes using SQL Authentication.

     

    I've read through everything on the forum and tried everything I could find.  Below are the answers from the most common suggestions posted here:

    ------------------------------------------------------------------------
    Version: SQL Server 2005 - 9.00.1399.06 (Intel X86)
    This is the Default & Only Instance of SQL 2005, named: MSSQLSERVER
    ------------------------------------------------------------------------

    1. The error message with the SQL Native driver (v 9.00.3042):
       Error 1326, SQL State 08001
       [Microsoft][SQL Native Client]Named Pipes Provider: Could not open a connection to SQL Server [1326]

     

    2. The error messgae with the ODBC SQL driver (v 03.85.1117):

       Error 1326, SQL State 01000
       [Microsoft][ODBC SQL Server Driver][Named Pipes]Connection Open (Connect())

       Connection Failed: SQL Error 17, State 08001
       [Microsoft][ODBC SQL Server Driver][Named Pipes]SQL Server does not exist or access denied.

     

    3. Local Connection on Win2003 Server works fine for IP & Named Pipes

     

    4. Remote Connection using SQL Authentication
       WORKS for IP, does not work for Named Pipes

     

    5. Absolutely *no firewalls* or blocked ports are being used on either machine

     

    6. Configuration Manager & Surface Area Config -- both enabled TCP/IP & Named Pipes
       Currently listening on ALL IP's.   The LOG reports:

        Server is listening on [ 'any' <ipv4> 1433].
        Server named pipe provider is ready to accept connection on [ \\.\pipe\sql\query ].
        Server is listening on [ 127.0.0.1 <ipv4> 1434].
        Dedicated admin connection support was established for listening locally on port 1434.

     

    7. Can telnet to server on 1433

     

    8. Status of NETSTAT on server:

       C:\...>netstat -ano|findstr 1433
         TCP    0.0.0.0:1433           0.0.0.0:0              LISTENING       656
       C:\...>netstat -ano|findstr 1434
         TCP    127.0.0.1:1434         0.0.0.0:0              LISTENING       656
         UDP    0.0.0.0:1434           *:*                                    2544

     

    9. Tried both named instance and unnamed instance (although I'm not sure
       if I'm passing the 'named' instance properly):

       Tested: \\MYSERVER\pipe\sql\query  
       Tested: \\MYSERVER\pipe\MSSQL$MSSQLSERVER\sql\query
       (although the named instance doesn't work on the server either)

     

    10.I've setup an alias for the named pipe connection.  This was based on a suggestion
       posted here, but didn't work.

     

    11.I've removed the shared-memory protocol, on another suggestion here, but no luck.

     

    12.If I execute "net use \\[server_name]\ipc$" it asks for credentials and if I
       enter the server credentials I have no problem with executing that command. 
       In other words the logged-on  user is not an authenticated member of the server;
       however, I'm not attempting  to use windows authentication, I'm using
       SQL Authentication.

    Trackback 3 Comment 0
    2009.12.18 10:27

    Prototype - Element class methods

    Prototype JavaScript framework 에서 제공하는 엘리먼트와 이벤트의 클래스 메서드 입니다. Script.aculo.usPrototype의 여러 메서드를 사용자가 편하게 이용할 수 있도록 각종 모션효과들을 만들어 줍니다. 하지만, 애써 클래스 메서드까지 익혀야 필요성이 있습니다. Script.aculo.us는 간접적인 효과만을 보여주기 때문입니다.

    우리는 지금까지 자바스크립트를 사용하여 아주 간단한 기능을 구현할 때에도 크로스-브라우저 지원을 위해 각종 환경변수를 만들어야 했습니다. Prototype은 이러한 문제를 일거에 해결해 줍니다. 그리고 메서드를 사용하면 코드의 문맥이 매우 아름다워(?)집니다. 엘리먼트를 숨기기위해 더이상 알록달록한 자바스크립트를 사용할 필요가 없어진 것입니다.

    자바스크립트 : document.getElementById("element").style.display = 'none';
    프로토타입(노헬퍼) : $('element').setStyle({display:none});
    프로토타입(헬퍼사용) : Element.hide('element');

    아주 간결하지요? 엘리먼트(Element)와 이벤트(Event) 클래스 메서드(Class Method)는 모션효과에서 요긴하게 사용되는 클래스입니다. 아래는 Prototype에서 현재 지원하는 클래스 목록입니다. (헬퍼 메서드를 사용하면 다소 간편한 반면, 처리속도가 급격히 떨어진다. - 글쓴이 주)

    Element class methods

    * Element.visible 엘리먼트의 활성화 여부 확인
    * Element.toggle 보임 또는 안보임으로 토글
    * Element.hide 지정한 엘리먼트를 숨긴다.
    * Element.show 지정한 엘리먼트를 보인다.
    * Element.remove 지정한 엘리먼트를 지운다.
    * Element.update 엘리먼트의 내용을 새로 고친다.
    * Element.getHeight 엘리먼트의 높이값을 구한다.
    * Element.classNames Class 이름을 리턴한다.
    * Element.hasClassName Class 이름을 확인한다.
    * Element.addClassName Class를 추가한다.
    * Element.removeClassName Class를 지운다.
    * Element.cleanWhitespace 내용없는것을 싹지운다.
    * Element.empty inner HTML을 비운다.(불확실)
    * Element.scrollTo 부드럽게 화면을 스크롤한다.
    * Element.getStyle 엘리먼트의 Style을 가져온다.
    * Element.setStyle 엘리먼트의 Style을 지정한다.
    * Element.getDimensions 높이, 너비값을 가져온다.
    Event class methods

    * Event.observe
    노드 값을 리턴하는 이벤트 헨들러를 지정한다.
    * Event.stopObserving
    observe 이벤트 핸들러를 지운다.
    * Event.element
    엘리먼트에 이벤트를 지정한다.
    * Event.isLeftClick (아직 안됨)
    * Event.pointerX
    포인터 x좌표에 반응하는 이벤트
    * Event.pointerY
    포인터 y좌표에 반응하는 이벤트
    * Event.stop
    모든 이벤트를 중지한다.
    * Event.findElement
    지정한 태그이름에서 노드값을 찾으면, 방아쇠가 당겨진다?
    * Event.unloadCache






















    출처 : firejune
    Trackback 0 Comment 0
    2009.12.15 20:38

    C# 4.0의 새로운 기능

    Written by 안재우(Jaewoo Ahn), 닷넷엑스퍼트(.netXpert)

     

    이미 많은 분들이 알고 계시겠지만, 드디어 C#의 버전이 4.0이 되었습니다. Microsoft의 버전 업그레이드질(?)에 지치셨을 분들에게 큰 압박이 되겠군요. -.-; 우선 4.0을 돌아보기 전에 그 과거부터 돌아봅시다.

     

    C# 1.0

    C# 1.0은 .NET 1.0 및 Visual Studio 2002와 함께 출시되었습니다. 한참 다른 언어(예: Java, C++)와 C#을 비교하는 얘기들이 많던 시절이었죠. 일부 사람들에게는 C계열 언어의 사생아(?)라는 혹평을 듣기도 했으나, 컴파일러/프로그래밍 언어의 귀재인 Anders Hajelsburg의 존재를 무시할 수는 없었을 겁니다.

     

    2002년~2003년 지음, Anders Hajelsburg는 C# 언어가 추구하는 방향을 'Most Modernized Programming Language'라는 개념으로 간단하게 정리했습니다. 말 그대로 현대에 나오는 프로그래밍 언어에 대한 개념 중 바람직한 것이 있다면 어느 언어보다도 빨리 적극적으로 흡수하는 프로그래밍 언어가 될 것이다라는 뜻입니다. (이 때부터 고생문이 훤하겠다 싶었습니다.)

     

    추억을 돌아보고 싶으신 분들은 MSDN에서 다음 페이지를 보시죠. 이 시절에는 C# 언어 사양을 온라인 버전으로도 볼 수 있었습니다.

    Visual C# Language (C#) - 영문

    Visual C# 언어(C#) - 한글

     

    C# 2.0

    C# 2.0은 .NET 2.0 및 Visual Studio 2005와 함께 출시되었습니다. C# 1.0 시절까지만 해도 프로그래밍 언어와 개발도구의 경계가 좀 애매하던 시절이었긴 하지만, 2.0에 와서야 언어와 개발도구를 명시적으로 분리를 하기 시작했습니다.

     

    C# 2.0에서 새로운 기능들은 다음 항목에 잘 정리되어 있습니다.

    What's New in the C# 2.0 Language and Compiler - 영문

    C# 2.0 언어 및 컴파일러의 새로운 기능 - 한글

     

    제 블로그에서도 C# 2.0에 대한 내용을 소개한 적이 있습니다.

    C# 2.0 및 VB.NET 2005의 새로운 기능 1 - Generic

    C# 2.0 및 VB.NET 2005의 새로운 기능 2

    C# 2.0 및 VB.NET 2005의 새로운 기능 3

     

    여러가지 변화가 있긴 하지만, C# 2.0에서의 가장 핵심적인 변화사항은 역시 Generic과 Partial Type이라고 봅니다. 이 두 가지로 인해 이후 C# 언어, 개발도구, 프레임워크 라이브러리 등에 많은 변화가 일어나게 되니까요. 아, 물론 Nullable TypeIterator,  Anonymous 메서드, Static 클래스Friend 어셈블리 등도 중요합니다.

     

    C# 3.0

    C# 3.0은 .NET 3.5 및 Visual Studio 2008과 함께 출시되었습니다. 무엇보다 C# 3.0에서의 가장 큰 변화는 PDC에서 소개되었던 LINQ의 도입과 var 키워드의 지원입니다. 물론 그 외에도 많은 변화가 있었습니다. 자세한 건 다음 내용을 읽어보시기 바랍니다.

    What's New in Visual C# - 영문

    Visual C#의 새로운 기능 - 한글

     

    LINQ나 var 키워드 외에 나머지 항목들이 흥미로운 것은 완전히 새롭게 등장한 개념이라기 보다는 기존 C#에서의 연장 선상을 보여주는 항목들이 많습니다. 또한 미래를 예측해볼 수도 있죠. ^^

     

    예를 들어, partial type (C# 2.0)에 이어 partial method가 등장했습니다. 현재 C# 4.0에는 포함되어 있지 않지만, 언젠가는 partial property도 등장하지 않을까 추측이 됩니다. (저 뿐만 아니라 여러 C# MVP들이 요청하는 기능 중 하나이죠)

    delegate -> anonymous method -> lambda expression으로의 발전 역시 흥미로운 항목입니다. 친절하게도 MSDN에서는 다음 예제를 통해 C# 1.0에서 C# 3.0까지의 대리자(delegate) 발전 과정을 한 눈에 보여줍니다. ^^

     

    class Test
    {
        delegate void TestDelegate(string s);
        static void M(string s)
        {
            Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            // Original delegate syntax required 
            // initialization with a named method.
            TestDelegate testdelA = new TestDelegate(M);

            // C# 2.0: A delegate can be initialized with
            // inline code, called an "anonymous method." This
            // method takes a string as an input parameter.
            TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };

            // C# 3.0. A delegate can be initialized with
            // a lambda expression. The lambda also takes a string
            // as an input parameter (x). The type of x is inferred by the compiler.
            TestDelegate testDelC = (x) => { Console.WriteLine(x); };

            // Invoke the delegates.
            testdelA("Hello. My name is M and I write lines.");
            testDelB("That's nothing. I'm anonymous and ");
            testDelC("I'm a famous author.");

            // Keep console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        } 
    }
    /* Output:
        Hello. My name is M and I write lines.
        That's nothing. I'm anonymous and
        I'm a famous author.
        Press any key to exit.
    */

    static 멤버 -> static 클래스 -> extension(확장) 메서드 역시도 그 연장 선상에 있습니다. 특히 프레임워크 라이브러리나 유틸리티 작업을 많이 하는 사람들에게 extension 메서드는 이제 거의 필수입니다.

     

    C# 4.0

    도대체 C# 4.0은 언제할건데? 자, 이제 시작합니다. -.-;;

    사실 이제와서 하는 얘기입니다만, 이 포스트를 쓸까 말까를 무척이나 망설였습니다. 왜냐하면, 사실 굳이 쓰지 않더라도 C# 언어 PM인 Mads Torgersen이 쓴 New Features in C# 4.0에 너무나 잘 정리되어 있기 때문입니다. 그러나, 영어에 약하신 분들을 위해 간단하게 정리해보도록 하겠습니다. ^^

     

    C# 4.0은 .NET 4.0 및 Visual Studio 2010과 함께 출시될 예정입니다. C# 4.0의 주요 테마는 이미 Dynamic in C# 4.0 포스트에서 소개했다시피 바로 dynamic입니다. 역시 얘기되었던 대로 C#은 여전히 static type 언어입니다만, dynamic type들과 상호 작용을 할 수 있는 기능들이 추가됩니다.

    사실 C# 4.0에서 dynamic 외에 Visual Basic과의 co-evolution이라는 테마를 가지고 있긴 한데, Microsoft 내부적으로는 의미가 있겠지만 외부적으로는 별 의미가 없는 것 같습니다. VB 사용자 분들에게는 죄송하지만, 최근 몇 년간 VB는 C#의 발전을 기를 쓰고 따라 잡기에 급급한 모습인 듯 합니다. 사실 C# 입장에서는 말이 co-evolution이지, '님하 살살 좀'을 외치는 VB도 따라 와 줄 수 있기에 '당분간 천천히 가줄께'라는 것 같거든요.

     

    New Features in C# 4.0에서는 C# 4.0의 새 기능들을 크게 4개 덩어리로 나누고 있습니다.

    - Dynamic lookup

    - Named and optional parameters

    - COM specific interop features

    - Variance

     

    Dynamic lookup

    Dynamic lookup에 대해서는 이미 Dynamic in C# 4.0에서 기본적인 내용을 소개했으니, 따로 설명하지 않겠습니다. 하지만 자세한 내용을 원하시는 분들은 New Features in C# 4.0에서 해당 부분을 반드시 읽어보시기 바랍니다. 특히 Open issues 절에서는 C# 4.0에서 dynamic과 관련한 제한사항들을 다루고 있습니다. DLR 관련 내용은 제쳐두더라도 extension 메서드를 사용할 수 없다거나, anonymous 함수를 메서드 호출 인수로 사용할 수 없어 LINQ 쿼리의 사용이 제한된다는 내용들은 주의할 항목입니다. 아쉽게도 이 제한사항들은 C# 4.0에서는 개선될 계획이 없습니다.

     

    Named and optional parameters

    이건 예제를 드는 것이 가장 설명이 쉬울 듯 합니다. 예를 들어, 다음과 같은 메서드가 있다고 가정합시다.

     

    public void M(int x, bool option1, string option2);

     

    그런데, 대부분 x 값만 넘겨주고, option1과 option2는 정해진 값만 넘기는 경우가 많다고 가정합시다.

     

    M(100, false, null);

     

    사실 이 경우, 다음과 같은 게 있으면 편할 겁니다.

     

    M(100); // option1 = false, option2 = null;

     

    하지만 현재는 이를 하기 우해서는 M 메서드를 일일이 다 오버로딩하는 수 밖에 없죠.

     

    public void M(int x) { M(x, false, null); }

     

    C# 4.0에서는 Optional Parameter가 도입되어 다음과 같이 처리가 가능합니다.

     

    public void M(int x, bool option1 = false, string option2 = null);

     

    이제 M 메서드는 다음과 같은 3가지 모두로 호출이 가능합니다.

     

    M(100, false, "Hello")

    M(100); // option1 = false, option2 = null;

    M(100, true); // option 2 = null;

     

    그런데 만약 중간에 있는 option1만 생략하고 싶다면 어떻게 해야 할까요?

    전통적인 방법이라면 다음과 같이 될 것입니다.

     

    M(100, , "Hello");

     

    하지만 C# 팀은 이 경우 가독성이 저하된다고 판단하고, named parameter라는 개념을 도입했습니다.

    M(1, option2: "Hello");

     

    named parameter를 쓸 경우, 메서드 인수의 순서를 바꿀 수도 있습니다.

     

    M(option2: "Hello", x: 100);

     

    COM specific interop features

    Dynamic과 named/optional parameter 덕에 COM API를 사용하는 것이 좀 더 편리해졌습니다. 그리고 COM에 국한된 몇 가지 interop 관련 기능이 추가되었습니다.

     

    먼저 COM에서는 Variant 타입을 쓰는 경우가 많은데, Variant의 경우 PIA(Primary Interop Assembly)에서는 object로 표현됩니다. 따라서 항상 object 타입을 자신이 사용하려는 타입으로 변환해야 하는 경우가 많았습니다. C# 4.0에서는 object 대신 dynamic을 사용해서 variant를 표현하는 것이 가능합니다. 예를 들자면 다음과 같죠.

     

    ((Excel.Range)excel.Cells[1, 1]).Value2 = "Hello";  // 기존 방식(cast 필요)

    excel.Cells[1, 1].Value2 = "Hello";  // C# 4.0(cast 필요없음)

     

    Optional Parameter의 지원으로, Default Parameter 등의 처리도 쉬워졌습니다. Missing.Value 노가다를 해보신 분들은 무슨 의미인지 이해하실 듯.. ^^

     

    excel.Workbooks.Add(Missing.Value); // 기존 방식

    excel.Workbooks.Add(); // C# 4.0

     

    또한 no-PIA라는 기능이 생겼습니다. 예전에는 COM 인터페이스를 strong type으로 interop하기 위해서는 PIA 어셈블리가 필요했는데, 덩치도 크다는 것도 있고 PIA는 별도로 배포되는 것이라 버전 관련 문제가 발생한다는 문제도 있었습니다. 그런데, no-PIA 기능은 PIA에서 실제로 프로그램에서 사용하는 부분만 떼서 별도 어셈블리가 아닌 호출 어셈블리 안에 넣어버리는 기능입니다. 따라서 이제 런타임 시에 더 이상 덩치 큰 PIA를 로드할 필요가 없어졌습니다.

     

    매개변수에서의 ref 키워드 문제도 있었는데, 이제 더 이상 COM API를 사용할 때 일일이 ref를 선언하지 않아도 됩니다. 컴파일러가 자동적으로 해당 작업을 처리해준다는군요.

     

    Variance

    통상적으로 Covariance는 원래 지정된 Type대신 그 Type에서 파생된 자식 Type을 사용할 수 있게 해주는 것을 말하며, Contravariance는 그 반대 개념, 즉 지정된 Type 대신 그 Type의 상위 부모 Type을 사용할 수 있게 해주는 것을 말합니다. (양쪽 다 별도의 명시적인 Casting이 없는 경우를 말합니다)

     

    예를 들어 다음과 같은 상속 구조가 있다고 가정합시다.

    class Animal;

    class Person : Animal;

    class Man : Person;

     

    어딘가에서 Person 타입을 요구한다고 가정합시다.

    이 때, Person 대신 Man을 사용할 수 있게 해주는 경우가 Covariance입니다.

    반대로 Person 대신 Animal을 사용할 수 있게 해주는 경우가 Contravariance가 되는거죠.

     

    그리고 만약 Covariance와 Contravariance가 둘 다 되지 않는 경우, 즉 위의 예제에서 무조건 Person 타입만 써야 하는 경우를 Invariant라고 합니다.

     

    C# 3.0까지는 Generic에 대해서는 Invariant였습니다. 그런데, C# 4.0에서는 Generic에 대해서 Covariance 및 Contravariance가 지원됩니다. 보다 자세한 내용은 New Features in C# 4.0의 내용이나, Eric Lippert의 블로그를 참조하시기 바랍니다. 

     

    C# 5.0?

    헉, 끔찍하시죠? ^^

    사실 C# 4.0이 논의될 시점에 C# MVP들과 Microsoft C# 팀 간에 별도의 뉴스 그룹이 만들어져서 매우 긴 토론이 있었습니다. C# Future나 Connect, Forum 등에서도 다양한 의견과 논의가 제시되었습니다.

     

    그 중 많이 얘기된 내용이 typedef의 지원이라거나, partial property, 언어/컴파일러 레벨에서의 AOP 지원, auto-implemented property에서 get이나 set 중 하나만 설정할 수 있게 해달라거나 backing store에 접근할 수 있게 해달라거나.. 다양한 요청이 있었습니다.

    위에서 일부 기능들의 경우, C# 4.0에 반영이 되지 않았을까 싶었는데, 결국 현재 C# 4.0은 위에서 소개한 기능들로 사양이 정해진 것 같습니다.

     

    프로그래밍 언어 얘기가 때로 재밌기도 한데, 어떻게 보면 너무 academic해보기도 하네요. C# 언어에 대한 얘기는 당분간 여기서 끝을 맺도록 하겠습니다. ^^


    출처 : http://blog.naver.com/saltynut/120097115880

    Trackback 5 Comment 0