잊지 않겠습니다.

public ContentResult ConvertJoinOrLeaveReasonsToContentResult(IQueryable<JoinOrLeaveReason> joinOrLeaveReasons, string fileName)
{
    string attachment = string.Format("attachment; filename={0}", fileName);

    Response.ClearContent();
    Response.AppendHeader("content-disposition", attachment);
    Response.Buffer = true;
    Response.Charset = string.Empty;

    TableStyle tblStyle = new TableStyle();
    tblStyle.BorderStyle = BorderStyle.Solid;
    tblStyle.BorderColor = Color.Black;
    tblStyle.BorderWidth = Unit.Parse("1px");

    TableItemStyle headerStyle = new TableItemStyle();
    headerStyle.BackColor = Color.LightGray;

    StringWriter sw = new StringWriter();
    HtmlTextWriter tw = new HtmlTextWriter(sw);

    tblStyle.AddAttributesToRender(tw);
    tw.RenderBeginTag(HtmlTextWriterTag.Table);

    tw.RenderBeginTag(HtmlTextWriterTag.Thead);
    {
        headerStyle.AddAttributesToRender(tw);
        tw.RenderBeginTag(HtmlTextWriterTag.Th);
        tw.Write("Date");
        tw.RenderEndTag();

        headerStyle.AddAttributesToRender(tw);
        tw.RenderBeginTag(HtmlTextWriterTag.Th);
        tw.Write("Comment");
        tw.RenderEndTag();
    }
    tw.RenderEndTag();

    Encoding encoding = sw.Encoding;
    tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
    foreach(JoinOrLeaveReason joinOrLeaveReason in joinOrLeaveReasons)
    {
        tw.RenderBeginTag(HtmlTextWriterTag.Tr);
        {
            tw.RenderBeginTag(HtmlTextWriterTag.Td);
            tw.Write(string.Format("{0:yyyy-MM-dd HH:mm:ss}", joinOrLeaveReason.CreateDate));
            tw.RenderEndTag();

            tw.RenderBeginTag(HtmlTextWriterTag.Td);
            tw.Write(string.Format("{0}", joinOrLeaveReason.Comment));
            tw.RenderEndTag();
        }
        tw.RenderEndTag();
    }
    tw.RenderEndTag();

    tw.RenderEndTag();

    ContentResult contentResult = new ContentResult();
    contentResult.ContentType = "application/ms-excel";
    contentResult.Content = sw.ToString();
    contentResult.ContentEncoding = Encoding.UTF8;

    return contentResult;
}

1. Response 설정 : Header, Buffer 설정
2. ContextResult의 데이터 설정
 - HtmlTextWriter를 이용한 Table Build
 - Table의 속성은 Attribute를 이용해서 설정해준다. 
3. 최종적으로 Context를 TextWriter를 이용해서 얻어준다.
Posted by Y2K
,
ActionResult의 Type중 ContentResult를 이용한다. 

먼저, Response 에 Buffer 설정과 Header 설정을 한다.
            Response.ClearContent();
            Response.AppendHeader("content-disposition", attachment);
            Response.Buffer = true;

다음 ContentResult의 결과를 넣어준다. 
<table>로 표현하는 경우에는 Excel에서 표로 읽는 것이 가능하다. 

            StringBuilder sb = new StringBuilder();
            sb.Append("<table>");
            sb.Append("<tr>");
            sb.Append("<td>Date</td>");
            sb.Append("<td>Comment</td>");
            sb.Append("</tr>");

            foreach(JoinOrLeaveReason joinOrLeaveReason in joinOrLeaveReasons)
            {
                sb.Append("<tr>");
                sb.AppendFormat("<td>{0:yyyy-MM-dd HH:mm:ss}</td>", joinOrLeaveReason.CreateDate);
                sb.AppendFormat("<td>{0}</td>", joinOrLeaveReason.Comment);
                sb.Append("</tr>");
            }
            sb.Append("</table>");

            ContentResult contentResult = new ContentResult();
            contentResult.ContentType = "application/wnd.ms-excel";
            contentResult.Content = sb.ToString();
            contentResult.ContentEncoding = Encoding.UTF8;

            return contentResult;

사용될 수 있는 Content-Type의 종류는 다음과 같다. 

1) Multipart Related MIME 타입
  - Content-Type : Multipart/related(기본형태)
  - Content-Type : Application/X-FixedRecord
  - Content-Type: Text/x-Okie; charset=iso-8859-1;

2) XML Media의 타입
 - Content-Type : text/xml
 - Content-Type : Application/xml
 - Content-Type : Application/xml-external-parsed-entity
 - Content-Type : Application/xml-dtd
 - Content-Type : Application/mathtml+xml
 - Content-Type : Application/xslt+xml

3) Application의 타입 
 - Content-Type : Application/EDI-X12:  Defined in RFC 1767 
 - Content-Type : Application/EDIFACT:  Defined in RFC 1767 
 - Content-Type : Application/javascript: Defined in RFC 4329 
 - Content-Type : Application/octet-stream: <-- 디폴트 미디어 타입은 운영체제 종종 실행파일, 다운로드를 의미
 - Content-Type : Application/ogg: Defined in RFC 3534 
 - Content-Type : Application/x-shockwave-flash: Adobe Flash files
 - Content-Type : Application/json: JavaScript Object Notation JSON; Defined in RFC 4627 
 - Content-Type : Application/x-www-form-urlencode <-- HTML Form 형태 

* x-www-form-urlencode와 multipart/form-data은 둘다 폼 형태이지만 x-www-form-urlencode은 대용량 바이너리 테이터를 전송하기에 비능률적이기 때문에 대부분 첨부파일은 multipart/form-data를 사용하게 된다. 

4) 오디오 타입
- Content-Type : Type audio: Audio 
- Content-Type : audio/mpeg: MP3 or other MPEG audio
- Content-Type : audio/x-ms-wma: Windows Media Audio;
- Content-Type : audio/vnd.rn-realaudio: RealAudio;  등등 
 

5) Multipart 타입(아카이브 또는 개체) 
- Content-Type : multipart/mixed: MIME E-mail; 
- Content-Type : multipart/alternative: MIME E-mail;
- Content-Type : multipart/related: MIME E-mail; Defined in RFC 2387 and used by MHTML(HTML mail) 
- Content-Type : multipart/formed-data : <-- 파일 첨부

6) TEXT 타입 
- Content-Type : text/css:  
- Content-Type : text/html:
- Content-Type : text/javascript 
- Content-Type : text/plain: 
- Content-Type : text/xml: 


7) 기타 MIMERPC 예제들 
가) HTTP with x/www-form-urlencoded 일반요청 
POST /some/resource HTTP/1.1
Content-type: application/x-www-form-urlencoded
0=example.getStateName&1=10023


[응답]
HTTP/1.1 200 OK
Content-type: text/plain
New York


나) HTTP x/www-form-urlencoded namedArgs getTeam
POST /some/resource HTTP/1.1
Content-type: application/x-www-form-urlencoded
0=example.getTeam&state=New York&sport=Baseball


[응답]
HTTP/1.1 200 OK
Content-type: multipart/mixed, boundary=B
--BYankees

--BMets

--B


다) HTTP x/www-form-urlencoded unicode addUser
POST /some/resource HTTP/1.1
Content-type: application/x-www-form-urlencoded
0=example.addUser&fname=Igna%ACio&lname=Sanchez


라) HTTP with multipart/form-data 요청
POST /some/resource HTTP/1.1
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"


Joe Blow


--AaB03x  
content-disposition: form-data; name="pics"; filename="file1.gif"
Content-type: image/gif
Content-Transfer-Encoding: binary
...contents of file1.gif...


--AaB03x--

[응답] 
HTTP/1.0 200 OK
Content-type: text/plain
OK


마) Uploading multiple files with unicode

POST /foo HTTP/1.0
Content-type: multipart/form-data, boundary=AaB03x


--AaB03x
content-disposition: form-data; name="field1"
Joe Blow


--AaB03x  <-- 여러개의 파일을 첨부할 때 
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y

--BbC04y  <-- 첫번째 첨부파일은 텍스트
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain; charset=UNICODE-1-1
Content-Transfer-Encoding: binary
... contents of some unicode file.txt ...


--BbC04y <-- 두번째 첨부파일은 이미지
Content-disposition: attachment; filename="file2.gif"
Content-type: image/gifContent-Transfer-Encoding: binary
...contents of file2.gif...

--BbC04y


----AaB03x--


바) XML and EMail 요청
HTP Request 
POST /x/foo/bar HTTP/1.0
reply-to-url: callback@domain.com
message-id: abc123
aynch: required0=getAuthorization&1="bobjones"


[응답] 
HTTP/1.0 200 OK
delivered-to: callback@domain.com
Content-length: 0
Mail/SMTP Response 
To: callback@domain.comFrom: mimeRPC@otherplace.com
in-reply-to: abc123
content-type: text/xml
<?xml version="1.0"?><this><is /><xml /></this>

Posted by Y2K
,

MVC Model은 매우 멋진 Web Model이고, View에서 표현하는 방법이 표준 Html을 기본적으로 사용하고 있기 때문에 매우 만족스러울 뿐 아니라, 디자이너와의 협업에서 뛰어난 적응도를 보여준다.

기존의 ASP .NET에서는 수 많은 서버 control들이 있었고, 이러한 서버 control들은 각각의 사용법 및 세부적인 디자인을 변경시키는 데에 있어서 많은 어려움을 가지고 있었던 것이 사실이다. (내가 잘 못하는 것도 있겠지만. –-;;)

MVC 모델을 사용하다가 자주 만드는 것 중 하나가 table인데, 각각의 데이터에 따라서 매번 다른 table을 반복작업을 하는 것이 싫어서 하나의 MVC user control을 만들었다.

먼저 LINQ를 이용한 데이터의 pagination을 제공하고, table에 각각 필요한 데이터를 Model로 선언한다. 그리고 table의 pagination은 AJAX call을 기반으로 한다. 

LINQ를 이용한 PagedList : 

   public interface IPagedList
    {
        int TotalCount { get; set; }
        int PageIndex { get; set; }
        int PageSize { get; set; }

        bool IsPreviousPage { get;  }
        bool IsNextPage { get;  }

        int TotalPageCount { get; set; }
    }

    public class PagedList<T> : List <T>, IPagedList
    {
        public PagedList(IQueryable<T> source, int index, int pageSize)
        {
            TotalCount = source.Count();
            PageSize = pageSize;
            PageIndex = index;
            TotalPageCount = TotalCount/PageSize;
            AddRange(source.Skip(index*pageSize).Take(PageSize).ToList());
        }

        public PagedList(List<T> source, int index, int pageSize)
        {
            TotalCount = source.Count();
            PageSize = pageSize;
            PageIndex = index;
            TotalPageCount = TotalCount / PageSize;
            AddRange(source.Skip(index * pageSize).Take(PageSize).ToList());
        }

        #region IPagedList Members

        public int TotalCount
        {
            get;
            set;
        }

        public int PageIndex
        {
            get;
            set;
        }

        public int PageSize
        {
            get;
            set;
        }

        public bool IsPreviousPage
        {
            get
            {
                return PageIndex > 0;
            }
        }

        public bool IsNextPage
        {
            get
            {
                return ( (PageIndex + 1) * PageSize ) < TotalCount;
            }
        }

        public int TotalPageCount { get; set; }

        #endregion
    }

    public static class Pagiation
    {
        public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize)
        {
            return new PagedList<T>(source, index, pageSize);
        }

        public static PagedList<T> ToPagedList<T>(this List<T> source, int index, int pageSize)
        {
            return new PagedList<T>(source, index, pageSize);
        }
    }

PagedTableData : Table에 필요한 데이터들
    public class PagedTableData
    {
        public List<string> ColumnHeaders { get; set; }     //Column Headers
        public string TableId { get; set; }                 //Html Id
        public PagedList<string[]> PagedList { get; set; }  //Page<tbody> datas
        public string ActionName { get; set; }              //Ajax call Action data
    }

PagedTable : MVC User Control 
PagedTable.ascx >>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PagedTable.ascx.cs" Inherits="SYNCmailAdmin.Views.Shared.PagedTable" %>
<div id="<%=ViewData.Model.TableId %>">
  <table class="mainTable" width="100%">
    <tr>
      <%foreach(string column in ViewData.Model.ColumnHeaders) { %>
      <th><%=column %></th>
      <%} %>
    </tr>
    
    <%foreach(string[] datas in ViewData.Model.PagedList) { %>
      <tr>
        <%foreach(string data in datas) { %>
          <td><%=data%></td>
        <%} %>
      </tr>
    <%} %>  
  </table>
  <div align="center">
    <%
      int pageIndex = ViewData.Model.PagedList.PageIndex;
      int startIndex = Math.Max(0, pageIndex - 5);
      int endIndex = Math.Min(pageIndex + 5, ViewData.Model.PagedList.TotalPageCount);
    %>
    <%if(ViewData.Model.PagedList.IsPreviousPage) { %>    
      <%for(int i = startIndex ; i < pageIndex ; i++){ %>
      <%=Ajax.ActionLink(i.ToString(), ViewData.Model.ActionName, new {pageIndex = i}, new AjaxOptions() { UpdateTargetId = ViewData.Model.TableId}) %>
      <%} %>
    <%} %>
    <strong><%=pageIndex%></strong>
    <%if(ViewData.Model.PagedList.IsNextPage) { %>
      <%for(int i = pageIndex + 1 ; i <= endIndex ; i++) { %>
      <%=Ajax.ActionLink(i.ToString(), ViewData.Model.ActionName, new {pageIndex = i}, new AjaxOptions() {UpdateTargetId = ViewData.Model.TableId}) %>
      <%} %>
    <%} %>    
  </div>
</div>


PagedTable.ascx.cs
    public partial class PagedTable : System.Web.Mvc.ViewUserControl<PagedTableData>
    {
    }


사용법은 다음과 같다. 
        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            HomeIndexDataModel dataModel = new HomeIndexDataModel() {PagedTableData = GetPagedData(null)};
            return View(dataModel);
        }

        public ActionResult GetPagedDataModel(int? pageIndex)
        {
            return View("PagedTable", GetPagedData(pageIndex));
        }

        private PagedTableData GetPagedData(int? pageIndex)
        {
            PagedTableData pageData = new PagedTableData();
            pageData.ColumnHeaders = new List<string>() {"FirstData", "SecondData"};
            pageData.TableId = "TableId";
            pageData.PagedList = TableTempDataGenerator.GetDatas()
                .ToPagedList(pageIndex == null ? 0 : pageIndex.Value, 10);
            pageData.ActionName = "GetPagedDataModel";

            return pageData;
        }

각 Column Header와 Table의 ActionResult 값을 얻어오는 함수명을 넣어주는 것으로 쉽게 해결 가능하다. 조금 제약사항이 되는 것이, GetPagedData에서 반드시 pageIndex라는 변수명으로 적어줘야지 되는 제약사항이 걸리게 된다. 

실행 결과는 다음과 같다.

Sample Source도 같이 올립니다. 필요하신 분들은 얼마든지 퍼가세요.
(이곳에 오시는 분들이 있으시다면.. ㅋㅋ ^^)
Posted by Y2K
,
1. XDocument to XmlDocument
            XDocument xDocument = new XDocument();
            XmlDocument xmlDoc = new XmlDocument();

            xmlDoc.Load(xDocument.CreateReader());

2. XmlDocument to XDocument
            StringReader sr = new StringReader(xmlDoc.InnerXml);
            XDocument xDocument = XDocument.Load(XmlReader.Create(sr));


Posted by Y2K
,

Pro Linq (06)

Book 2009. 1. 13. 10:51
LINQ를 이용한 XML의 검색방법 역시 기존의 LINQ와 동일하다. 
특징적으로 이야기할 수 있는 사항은 각각의 XML의 element, attribute, comment 등으로 검색이 가능하다는 점으로, DB LINQ나 Object LINQ에서 사용하는 것과 같이 XML의 특성으로 검색이 가능하다. 

조금 복잡한 LINQ query를 하나 예시로 든다면.. 

            var biddata = from b in bids.Descendants("bid_tuple")
                          where double.Parse(b.Element("bid").Value) > 50
                          join u in users.Descendants("user_tuple")
                              on b.Element("userid").Value equals u.Element("userid").Value
                          join i in items.Descendants("item_tuple")
                              on b.Element("itemno").Value equals i.Element("itemno").Value
                          select new
                                     {
                                         Item = b.Element("itemno").Value,
                                         Description = i.Element("description").Value,
                                         User = u.Element("name").Value,
                                         Date = b.Element("bid_date").Value,
                                         Price = b.Element("bid").Value
                                     };
            foreach(var bd in biddata)
            {
                Console.WriteLine("{0, -12} {1, -12} {2, -6}, {3, -14}, {4:C, 10}", bd.Date, bd.User, bd.Item,
                                  bd.Description, bd.Price);
            }

보면 재미있는 내용중 하나가 select 구문에서의 새로운 Class의 생성이다. 이 Class는 따로 선언되어있지 않으며, 각각의 Property의 이름을 각각 적어서 생성하게 된다. 이 부분은 C# 4.0에서 dynamic programming과 비슷하게 보이지만 dynamic으로 선언된것과는 다르게 사용된다. var로 선언되어 익명 class로 생성된 상태이다. 

여기에서 bd에 대해 type을 얻어보면 다음과 같다.
<>f__AnonymousType1`5[String, String, String, String, String]
사용된 Item, Description, User, Date, Price에 대해서 5개의 Property를 갖는 익명 Class의 선언으로 되어 있는 것을 알 수 있다.
Posted by Y2K
,
미네르바의 구속소식을 들으면서 말 한마디로 잡아가는 경찰국가가 되어가고 있구나.. 라는 생각이 들면서 어떻게 이런 일이 생길 수 있을까 하는 어이가 상실 되었다. 

소통을 열심히 말하면서, 절대로 소통하지 않는 국가에서...
결국은 자신의 이야기를 하는 사람들은 모두 잡아 가두는 세상이 되어가고 있다.

검찰은 미네르바의 이야기로 외화보유액에 영향을 끼쳤다고 하는데. 그렇다면 이네르바의 주가 3000이야기는 어찌 되는건가?

그리고, 검찰은 증명할 수 있을까? 미네르바의 글이 외화보유액에 영향을 미쳤다는 것에?
검찰이 아무리 떡찰이라지만... 이건 정말로 아니다. 

우리나라는 민주주의 국가다. 사람들은 자유롭게 자신의 소신을 피력할 수 있고, 자신의 이야기를 이야기할 수 있는 국가이다. 그런데, 지금 돌아가는 것을 보면 중국이나 북한과 별 다를것이 없는 통제 국가로 변화하고 있다는 생각이 든다. 

올해의 한자 성어로 양두구육(羊頭狗肉 )을 뽑는 것이 맞을 것 같다.
겉으로는 자유민주주의 국가를 외치면서, 통제국가가 되어가고 있으며..
겉으로는 소통을 외치면서, 지하 벙커에 들어가서 소통을 거부하고 있는...

이런 말도 안되는 상황을 만든 우리나라 국민들이 바보라는 생각만이 든다..

PS:검찰이 결국은 20억달라라는 정확한 수치를 대밀었다. 그 수치를 어떻게 증명하는지 지켜보겠어.
Posted by Y2K
,

어제 책을 읽다가 기존에 사용하고 있던 일반적인 text 형태의 Error reporter를 조금 예쁘게 만들어보고 싶다는 생각이 들어서 급조한 소스. 

xslt에 대한 포멧을 아직은 정확히 알지를 못해서 좀더 공부를 해볼 필요가 있을것 같다.
무엇보다 모든 일에 대한 표준화를 제공해줄수 있다는 점에서 가장 큰 장점을 가지고 있는듯. 

Class Source
    using System.Net.Mail;
    using System.Xml;
    using System.Xml.Linq;
    using System.IO;
    using System.Xml.Xsl;

    public class ExceptionReporter
    {
        private readonly Exception reportedException;
        public ExceptionReporter(Exception ex)
        {
            reportedException = ex;
        }

        public string ConvertXml()
        {
            XDocument xDocument = new XDocument();
            XElement rootElement = new XElement("Exceptions");
            XElement element = new XElement("ExceptionInfo");
            
            rootElement.Add(element);
            element.Add(new XElement("ExceptionType", reportedException.GetType().ToString()));
            element.Add(new XElement("Message", reportedException.Message));
            element.Add(new XElement("Source", reportedException.Source));
            element.Add(new XElement("TargetSite", reportedException.TargetSite));
            element.Add(new XElement("StackTrace", reportedException.StackTrace));
            xDocument.Add(rootElement);
            
            string xsl = File.ReadAllText("ErrorReporter.xslt");
            XDocument transformedDoc = new XDocument();
            using(XmlWriter xmlWriter = transformedDoc.CreateWriter())
            {
                XslCompiledTransform transform = new XslCompiledTransform();
                transform.Load(XmlReader.Create(new StringReader(xsl)));
                transform.Transform(xDocument.CreateReader(), xmlWriter);
            }
            return transformedDoc.ToString();
        }

        public bool SendEmailToManager()
        {
            SmtpClient client = new SmtpClient("smtpAddress");
            MailMessage mailMessage = new MailMessage();
            mailMessage.Subject = "Subject";
            mailMessage.IsBodyHtml = true;
            mailMessage.Body = ConvertXml();
            mailMessage.From = new MailAddress("fromMailAddress");
            mailMessage.To.Add(new MailAddress("toMailAddress"));
            client.Send(mailMessage);

            return true;
        }
    }


XSLT File
<?xml version='1.0' encoding='utf-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
  <xsl:output method='html'/>
  <xsl:template match='/'>
    <body>
      <xsl:apply-templates/>
    </body>    
  </xsl:template>

  <xsl:template match='ExceptionInfo'>
    <table width="100%" bgcolor="#d1d9e4" cellspacing="1" cellpadding="3">
      <tr>
        <td bgcolor="#666666" colspan="2">
          <font face="Arial" color="white">
            <b>Error Information</b>
          </font>
        </td>
      </tr>
      <tr bgcolor="#d0d0d0">
        <th>Parameter</th>
        <th>Data</th>
      </tr>
    <xsl:apply-templates />      
    </table>
  </xsl:template>
  <xsl:template match='ExceptionType'>
    <tr valign="top" bgcolor="white">
      <td>Exception Type</td>
      <td><xsl:apply-templates/></td>
    </tr>
  </xsl:template>  
  <xsl:template match='Message'>
    <tr valign="top" bgcolor="#f4f4f4">
      <td>Message</td>
      <td>
        <xsl:apply-templates/>
      </td>
    </tr>
  </xsl:template>
  <xsl:template match='Source'>
    <tr valign="top" bgcolor="white">
      <td>Source</td>
      <td><xsl:apply-templates/></td>
    </tr>
  </xsl:template>
  <xsl:template match="TargetSite">
    <tr valign="top" bgcolor="#f4f4f4">
      <td>TargetSite</td>
      <td><xsl:apply-templates/></td>
    </tr>    
  </xsl:template>
  <xsl:template match="StackTrace">
    <tr valign="top" bgcolor="white">
      <td>Stack Trace</td>
      <td><xsl:apply-imports/></td>
    </tr>
  </xsl:template>
</xsl:stylesheet>


사용 예제 : 
            try
            {
                File.ReadAllText("C:\\afwqerqwer.txt");
            }
            catch(Exception ex)
            {
                ExceptionReporter reporter = new ExceptionReporter(ex);
                reporter.SendEmailToManager();
                //Console.WriteLine(reporter.ConvertXml());
            }


결과:




Posted by Y2K
,

Pro LINQ (05)

Book 2009. 1. 9. 01:16
LINQ를 이용하면 XML의 검색 뿐 아니라, XML의 조작도 간단한 코드로 가능하다. 예전처럼 DataSet을 지정해서, DataSet에 Read Position을 잡아서 해주는 것이 아닌 직관적인 Method을 이용해서 XML의 조작이 가능하게 된다. 

기본적으로 사용되는 Method의 종류는 다음과 같다. 

추가 : Add(), AddFirst(), XNode.AddAfterSelft()
삭제 : Remove(), RemoveAll()
변경 : Update(), Replace(), ReplaceAll()

여기에서 재미있는 것이 ReplaceAll()의 경우인데, 모든 Element의 이름을 기준으로 값을 일괄적으로 변경이 가능하게 된다. 잘만 찾으면 재미있는 결과를 가지고 올 수 있을 것 같다. 

LINQ를 이용한 XML의 검색 및 변경에서 조금 특이한 경우가 XAttribute인데, XAttribute의 경우에는 W3C XML DOM API와 다르게 XAttribute를 통해서 이루어지게 된다. Element, Comment, Processing 과는 아주 약간 다른 조작을 행하게 되지만, 기본적으로 큰 차이는 존재하지 않는다. 

LINQ 및 기존의 XmlDocument에서 재미있는 내용중 하나는 각 Xml Element들에 대한 Event인데, XDocument에서도 기본적으로 Changing, Changed에 대해서 Event를 제공하고 있다. 

XDocument의 생성 및 변경을 통해서 재미있는 일을 할 수 있는데, 그 중 가장 재미있어보이기도 하고, 무엇보다 자주 사용할 수 있어보이는 XSLT transformation에 대한 재미있는 소스를 하나 적어보고 오늘 글을 마쳐볼려고 한다. ^^

XSLT :
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:output method='html'/>
<xsl:template match='/'>
<html>
<head>
<title>Student Directory</title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>

<xsl:template match='student_list'>
<h3>Student Directory for example.edu</h3>
<xsl:apply-templates />
</xsl:template>
<xsl:template match='name'>
<p>Name: <xsl:apply-templates /></p>
</xsl:template>
<xsl:template match='major'>
<p>Major: <xsl:apply-templates /></p>
</xsl:template>
<xsl:template match='phone'>
<p>Phone: <xsl:apply-templates /></p>
</xsl:template>
<xsl:template match='email'>
<p>Email: <xsl:apply-templates /></p>
</xsl:template>
</xsl:stylesheet>

XML : 
<student_list>
<student>
<name>John</name>
<major>Math</major>
<phone>010</phone>
<email>student1@mail.co.kr</email>
</student>
<student>
<name>Tom</name>
<major>English</major>
<phone>0001389</phone>
<email>student2@mail.co.kr</email>
</student>
</student_list>

이 둘에대한 transformation에 대한 소스는 다음과 같다. 
            XDocument xDocument = XDocument.Parse(xml);
            XDocument transformedDoc = new XDocument();
            using(XmlWriter xmlWriter = transformedDoc.CreateWriter())
            {
                XslCompiledTransform transform = new XslCompiledTransform();
                transform.Load(XmlReader.Create(new StringReader(xsl)));
                transform.Transform(xDocument.CreateReader(), xmlWriter);
            }
            Console.WriteLine(transformedDoc);


그리고 멋진 실행결과는 다음과 같다. ^^


이걸 이용하면 MailTemplate들이나 Table 표시 포멧을 만들어주는데 표준적인 방법을 제시할 수 있다. (당장 써보고 싶은 기능이다.)
Posted by Y2K
,

Pro LINQ (04)

Book 2009. 1. 9. 01:02
XML을 얻는다면 일반적으로 XmlDocument를 선언하고, XmlDocument에서 지정된 Node의 선택과 node에 대한 Tree 구조로 탐색을 하게 된다. 그러나, LINQ를 사용하게 될 경우에는 이러한 Tree 구조 탐색에 별 의미가 없게 된다. 

XElement에서 각 이름에 대한 검색 및 각 속성에 대한 검색을 모두 지원하고 있다. 다음 소스의 결과는 다음과 같다. 

        public void DoForAncestors()
        {

            XDocument xDocument = new XDocument(
                new XElement("BookParticipants",
                             new XElement("BookParticipant",
                                          new XAttribute("type", "Author"),
                                          new XElement("FirstName", "Joe"),
                                          new XElement("LastName", "Rattz")),
                             new XElement("BookParticipant",
                                          new XAttribute("type", "Editor"),
                                          new XElement("FirstName", "Ewan"),
                                          new XElement("LastName", "Buckingham"))));
            Console.WriteLine("Original XML : ");
            Console.WriteLine(xDocument.ToString());
            Console.WriteLine();
            Console.WriteLine("Search node : name == FirstName");
            var elements = xDocument.Element("BookParticipants").Descendants("FirstName");
            foreach(var element in elements)
            {
                Console.WriteLine(element.Value);
            }
        }



기존의 경우에는 모든 node의 트리 구조를 알고 있어서 xpath를 이용해서 SelectSingleNode 또는 SelectNodeList를 이용해서 각 값을 구해주었지만, LINQ를 이용하면, node에 대한 이름만을 이용해서 얻는 것이 가능하다. 그리고 얻어진 node에서 각 Parent를 얻어내는 것도 가능하다. 

여기에서 한가지 의문이 생기는 것이... 기존의 XmlDocument의 경우에는 거의 필요가 없는 class가 되어버리는 경향이 있는 것 같다. XmlDocument에서 이용하던 거의 모든 method들을 XDocument에서 제공하고 있으며, LINQ 및 보다더 선언적인 XML을 선언할 수 있는 것까지. 지금은 잘 모르겠지만.. 아무래도 XmlDocument를 앞으로는 사용할 일이 그다지 없지 않을까 라는 성급한 생각도 해보게 된다. (물론 .NET Framework 2.0에서의 경우는 또 다르다.;)

XML을 검색을 할때에 Element, Processing, Comment에 대한 검색을 하는 것 역시 LINQ에서 지원이 가능하다. 
또한 선언적인 Type을 넣어줄 수 있기 때문에 코드의 가독성 역시 같이 높아진다. 

        public void SearchForType()
        {
            XDocument xDocument = new XDocument(
                        new XElement("BookParticipants",
                         new XElement("BookParticipant",
                                      new XAttribute("type", "Author"),
                                      new XComment("This is XComment"),
                                      new XProcessingInstruction("Processing", "New Processing"),
                                      new XElement("FirstName", "Joe"),
                                      new XElement("LastName", "Rattz")),
                         new XElement("BookParticipant",
                                      new XAttribute("type", "Editor"),
                                      new XElement("FirstName", "Ewan"),
                                      new XElement("LastName", "Buckingham"))));
            Console.WriteLine(xDocument.ToString());
            Console.WriteLine();

            var elements = xDocument.Element("BookParticipants").
                Elements("BookParticipant").Nodes().OfType<XComment>();

            foreach(var element in elements)
            {
                Console.WriteLine(element);
            }
        }



Posted by Y2K
,
XElement를 이용하면 XmlDocument를 이용한 XmlDocument의 생성보다 편하고 직관적인 방법을 제공할 수 있다.

XElement - Element 생성
XComment - Comments 생성
XDeclaration - Declaration 생성

    public class XmlLinq2
    {
        public void CreateXmlDocument()
        {
            XNamespace nameSpace = "http://www.hostway.co.kr";
            XElement xBookParticipant = new XElement(nameSpace + "BookParticipant");
            XAttribute bookAttribute = new XAttribute("type", "Author");
            XElement xFirstname = new XElement(nameSpace + "FirstName", "Joe");
            XElement xLastName = new XElement(nameSpace + "LastName", "Rattz");
            XComment xComment = new XComment("This is XComment");

            xBookParticipant.Add(bookAttribute);
            xBookParticipant.Add(xComment);
            xBookParticipant.Add(xFirstname);
            xBookParticipant.Add(xLastName);

            Console.WriteLine(xBookParticipant);
        }

        public void FindDatas()
        {
            XNamespace nameSpace = "http://www.hostway.co.kr";
            XElement xBookParticipant = new XElement(nameSpace + "BookParticipant", 
                                                     new XElement(nameSpace + "FirstName", "Joe"),
                                                     new XElement(nameSpace + "LastName", "Rattz"));
            Console.WriteLine(xBookParticipant.ToString());
            Console.WriteLine(xBookParticipant);
        }
    }

: 기초책을 볼때마다 느끼는 거지만.. 언제나 기본인 내용을 모르고 있는 내 모습이 많이 부끄러워질때가 많은 것 같다. 


Posted by Y2K
,