تولید مکانیزه صفحات استاتیک به کمک XML
اکنون روش مورد نظر در این مقاله را بررسی میکنیم. در این روش به جای اینکه یک صفحه دینامیک از جنس ASP یا ASP.NET بسازیم که محتوای هر خبر یا مقاله را نمایش بدهد، یک صفحه Template از جنس XSL میسازیم و محل قرارگرفتن دادههای فیلدهای هر رکورد را داخل صفحه مشخص میکنیم. برای ساختن صفحات Template مذکور باید ابتدا صفحات وب را به فرمت XHTML تبدیل کنیم. سپس با قراردادن فیلدهای موردنظر، صفحه XHTML را به XSL تبدیل کنیم.
تبدیل HTML به XHTML
ساختن صفحات XHTML بسیار ساده است. کافی است صفحه معمولی خود را بسازید و محل قرارگرفتن فیلدها را مشخص کنید (مثلا از طریق تایپ کردن نام هر فیلد) و سپس به کمک یک مبدل HTML به XHTML صفحه وب خود را به فرمت سازگار با XHTML تبدیل کنید. برنامهای مانند Dreamweaver به کمک یک فرمان ساده، این کار را به راحتی آب خوردن انجام میدهد!
تفاوت یک HTML معمولی با یک HTML سازگار با فرمت XHTML چندان زیاد نیست. درواقع کنترلی باید روی صفحه انجام شود تا مطمئن شویم که tagهای صفحه در قیاس با قواعد XML اصطلاحا well-form هستند. مثلا در فرمت XML هر tag که شروع میشود باید حتما پایانی داشته باشد. مثل <table></table> اگر بعضی از tag ها قرار است که تنها باشند (مثل <br>) این tag ها باید به صورت مثلا </br> اصلاح شوند. چند تغییر جزئی دیگر نیز باید صورت گیرد تا صفحه کاملا با فرمت XHTML سازگار باشد. حتی اگر یک کاراکتر هم ناسازگار با قواعد XML باشد، امکان استفاده از روش توضیح داده شده در این مقاله وجود نخواهد داشت. ضمنا، کلاس و شئ XML در داتنت بعضی از کاراکترها و ترکیبهای کاراکتری را نیز نمیپذیرد. مثلا ;nbsp& که نمایانگر یک space یا فاصله است، از دید مفسر XML در دات نت مجاز نیست و به جایش باید همان کاراکتر space را تایپ کنید. نگران نباشید. محدودیتهایی که در این زمینه وجود دارند بسیار اندک هستند و تقربیا صفحه خود را با حدود یکی دو درصد تغییر میتوانید به یک XHTML قابل استفاده برای کلاس XML تبدیل کنید.
یادآوری مهم : ضروری است که صفحات فارسی خود را از جنس یونیکد و با کاراکترست utf-8 بسازید. زیرا شئ xml در دات نت تنها در این صورت میتواند اطلاعات فارسی را بدون مشکل پردازش کند.
تبدیل XHTML به XSL
این قسمت از کار مهمترین قسمت ساختن Template مورد نظر است. برای اینکار باید با استاندارد XSL و تکنیکها و زبان آن آشنا شوید. XSL بحث مفصلی دارد و برای آموختن آن کتابهای متعددی چاپ شده است که لازم است حداقل یکی از آنها را به عنوان مرجع دم دست داشته باشید. من برای سهولت کار، چند تکنیک ساده و پرکاربرد XSL را که در این مقاله نیاز داریم، داخل فایل مثالی که ضمیمه مقاله است، پیاده کردهام که میتوانید از آنها به عنوان الگو استفاده کنید. XSL به معنی eXtensible Stylesheet Language است. XSL در دنیای فناوری XML مشابه CSS در دنیای HTML است. اگر با CSS ها آشنایی داشته باشید، درک کاری که XSL انجام میدهد چندان برایتان مشکل نخواهد بود. همانطور که از CSS برای فرم دادن به صفحات وب استفاده میکنیم، از XSL نیز برای فرم دادن به محتویات فایلهای XML استفاده میکنیم.
تکنیکهایی که برای پردازش فایلهای XSL و قراردادن دادههای XML استفاده میشود اصطلاحا XSLT نام دارد (به انتهای عبارت XSL کلمه Transformation را بیفزایید).
قلب فرآیندی که اتفاق میافتد به این شرح است :
- دادههای شما، یعنی اطلاعات رکوردهای خبر و مقاله باید به فرمت XML درآیند.
- کد برنامه، فایل XSL شما را باز میکند و آن را برای یافتن tag های مخصوص نمایش فیلدهای اطلاعاتی جستجو میکند و به محض پیدا کردن محل فیلد، اطلاعات آن را جایگزین میکند.
به عنوان مثال عبارت <”xsl:value-of select=”ArticleTitle> معادل گزینهای است که چند پاراگراف بالاتر به عنوان روش نمایش فیلد ArticleTitle در صفحه ASP.NET شرح دادم. به عنوان نمونه دیگر، در زبان XSL با استفاده از syntax زیر میتوانیم یک حلقه بسازیم :
…
</xsl:for-each>
که حلقه روی فیلدهای جدول Table تکرار میشود.
کاری که ما در برنامه خود انجام میدهیم اینست که ابتدا رکوردهای بانک اطلاعاتی را با استفاده از شئ xml در دات نت به XML تبدیل میکنیم سپس با استفاده از کلاس xslt یک فایل XSL را بازمیکنیم و داده ها را درون آن میریزیم و حاصل را به صورت یک HTML معمولی ذخیره میکنیم. همین!
کد برنامه تولید مکانیزه صفحات وب با استفاده از ASP.NET
کاری که ما باید انجام دهید اینست که یک صفحه ASP.NET بسازیم که عملیات تولید صفحه استاتیک را به طور خودکار انجام دهد تا بتوانیم حاصل فرآیند را خیلی ساده روی سایت publish کنیم. البته میتوانیم ASP.NET را طوری بنویسیم که خودش در محل مشخص شده توسط ما صفحات HTML تولید شده را save کند. در اینصورت باید میزبان شما قابلیت استفاده از فناوری ASP.NET را در اختیار شما قرار دهد وگرنه مجبورید صفحات را روی دستگاه خودتان تولید کنید و بعد حاصل کار را به صورت دستی یا به هر روش دیگر upload کنید.
و حالا سورس کد :
( من از VB.NET استفاده کرده ام ولی خودتان میدانید که چقدر تبدیل آن به #C آسان است)
۱- برای اجرای برنامه لازم است کلاسهای مورد نیاز را import کنیم :
Imports System.Data
Imports System.Data.SqlClient
Imports System.IO
Imports System.IO.Path
Imports System.Xml
Imports System.Xml.Xsl
۲- من فرض را براین میگذارم که شما یک datagrid خودتان ساختهاید و می توانید در این صفحه ASP.NET رکوردهای اخبار و مقالاتتان را مدیریت کنید. من در این مقاله روش اینکار را توضیح نمیدهم چون روشش خیلی آسان و سرراست است و در همه سایتهای مربوط به ASP.NET میتوانید یک دوجین مقاله درباره روش نمایش رکوردهای بانک اطلاعاتی توسط datagrid پیدا کنید. شما باید همچنین در این datagrid امکان select کردن یک رکورد (جهت انجام پردازشها مورد نیاز این مقاله) را اضافه کنید. این کار نیز آسان است و من قصد آموزش آن در اینجا را ندارم.
۳- همه عملیات را یک تابع بسیار ساده و جادویی انجام میدهد
‘single node———————–
Dim myData As DataSet = GetDataSet()
Dim doc As XmlDataDocument = New XmlDataDocument(myData)
Dim FileName As String
Dim ln As Integer
Dim node As XmlElement = doc.DocumentElement.SelectSingleNode(“//Table[ID=" + RecordID.ToString + "]“)
If Not node Is Nothing Then
Dim doc2 As XmlDocument = New XmlDocument
doc2.LoadXml(node.OuterXml)
‘Generate HTML file name
FileName = RecordID.ToString
Dim HTMLsPath As String = Server.MapPath(“\”) + “Articles\” + Trim(doc2.SelectSingleNode(“//Category”).InnerText) + “\”
‘Transform
Dim xslt As XslTransform = New XslTransform
xslt.Load(HTMLsPath + “temp.html”)
Dim writer As XmlTextWriter = New XmlTextWriter(HTMLsPath + FileName + “.htm”, System.Text.Encoding.UTF8)
‘writer.Formatting = Formatting.Indented
‘writer.Indentation = 2
xslt.Transform(doc2, Nothing, writer, Nothing)
writer.Close()
End If
Return True
End Function
از جزئیات این تابع نترسید. خیلی ساده در سه مرحله، یعنی در سه سوت (!) کار را تمام میکند.
من این تابع را بعد از یک ماه تلاش مداوم به این صورت خلاصه و مفید نوشتم و خیالتان راحت باشد که صد در صد کار میکند. فقط توجه کنید که نام فیلدهای استفاده شده در این تابع با توجه به نامگذاری فیلدهای پایگاه داده خودم صورت گرفته است و شما باید این جزئیات تابع را مطابق نیاز خود تغییر دهید. مراحل سه گانه فوق به این شرح هستند :
۱- خواندن xml از روی dataset
۲- تولید نام فایل خروجی
۳- تبدیل (Tarnsform)
سومین مرحله که مهترین مرحله است، درواقع همان عملیات XSLT است. در اینجا باید گفت دست طراحان معماری دات نت درد نکند که با چهار خط کد میتوان یک فرآیند XSLT را کامل کرد!
مرحله اول :
در این مرحله از روی dataset یک xml میسازیم :
Dim myData As DataSet = GetDataSet()
Dim doc As XmlDataDocument = New XmlDataDocument(myData)
Dim FileName As String
Dim ln As Integer
Dim node As XmlElement = doc.DocumentElement.SelectSingleNode(“//Table[ID=" + RecordID.ToString + "]“)
توجه کنید که من روش ساختن dataset را توضیح ندادهام ولی شما خودتان میتوانید تابع GetDataSet را مطابق نیازتان بنویسید. این کار یکی از آسانترین و ابتدایی ترین روشهای کار با رابط برنامهنویسی ADO.NET است (لابد میدانید که دو روش عمده برای خواندن اطلاعات از بانک اطلاعاتی وجود دارد، یکی شئ DataReader و دیگری شئ DataSet) و من در اینجا برای اجتناب از طولانی شدن مقاله، از توضیح دادن آن صرفنظر کردم.
شئ جادوئی XmlDataDocument کارش تبدیل dataset به XML است. خط آخر این مرحله یک شئ XmlElement از روی همان رکوردی که میخواهیم اطلاعاتش را بخوانیم میسازد. دقت کنید که چطوری نام جدول پایگاه داده و id رکورد را که به صورت input وارد تابع کردهام، به این شئ خورانده میشود.
مرحله دوم :
حالا باید از روی خروجی xml شئ مذکور یعنی node.OuterXml یک شئ XmlDocument بسازیم و dataset را کنار بگذاریم (توجه کنید که در سورس اصلی تابعی که نوشتهام، مرحله دوم و سوم را داخل یک شرط if قرار دادهام تا اگر رکورد مورد نظر پوچ بود این دو مرحله اجرا نشوند):
doc2.LoadXml(node.OuterXml)
‘Generate HTML file name
FileName = RecordID.ToString
Dim HTMLsPath As String = Server.MapPath(“\”) + “Articles\” + Trim(doc2.SelectSingleNode(“//Category”).InnerText) + “\”
در واقع یک XmlDocument در کد برنامه دات نت، معرف یک xml است. و در تمام این مقاله هرجا گفتم کلاس (یا شئ xml در دات نت) بیشتر مقصودم همین شئ بود. البته کل اشیاء بکار رفته در این مقاله از کلاس مادر System.xml و System.xml.xsl مشتق شده اند.
در ادامه این مرحله من کمی با اطلاعات فیلد RecordID بازی کرده ام تا یک نام دلخواه و یک path مناسب برای دخیره کردن فایل HTML نهایی جور کنم. همچنین اگر خواستید روی دادههای رکورد مورد نظر، پیش از قرارگرفتن در XSL تغییری انجام دهید، مثلا تاریخ میلادی را به شمسی تبدیل کنید، جایش همین مرحله است. با استفاده از syntax زیر میتوانید به محتویات یک فیلد از این رکورد دسترسی داشته باشید (قابل خواندن و نوشتن):
مرحله سوم :
در این مرحله عملیات جادویی تبدیل صورت میگیرد!
خط اول یک XslTransform معرفی کرده ام. این شئ یک تابع بدقلق اما فوقالعاده نیرومند دارد که حدود ده تا تعریف overload دارد. یعنی میتوان این تابع را به چندین روش فراخوانی کرد. پیدا کردن حالت مناسب برای این تابع خودش مکافاتی بود (!) ولی حالا نتیجه کار بسیار ساده از آب درآمده است. من فایل XSL که به روش توضیح داده شده ساختم را به اسم temp.html در همان مسیری (path) که میخواهم فایل خروجی را ذخیره کنم قرار دادم. ابتدا با استفاده از تابع load این فایل را باز میکنم. سپس با استفاده از یک شئ جادویی دیگر به نام XmlTextWriter یک مجرای خروجی (یک stream) برای تابع Transform فراهم میکنم:
Dim xslt As XslTransform = New XslTransform
xslt.Load(HTMLsPath + “temp.html”)
Dim writer As XmlTextWriter = New XmlTextWriter(HTMLsPath + FileName + “.htm”, System.Text.Encoding.UTF8)
‘writer.Formatting = Formatting.Indented
‘writer.Indentation = 2
xslt.Transform(doc2, Nothing, writer, Nothing)
writer.Close()
ذکر چند نکته در اینجا ضروری است:
۱- آدرسی که در مرحله دوم ساختم را به XmlTextWriter میدهم.
۲- فرمت یونیکد را نیز برای XmlTextWriter مشخص میکنم.
۳- اگر دلم خواست، از property فرمتینگ نیز استفاده میکنم:
writer.Indentation = 2
این property خیلی جالب و در عین حال دردسرساز است. کارش اینست که خروجی HTML شما را برای خواندن سورس آن فرم بدهد. اگر اینکار را نکنید، XmlTextWriter تمام فایل HTML شما را در یک خط (!) مینویسد. هنگام نمایش HTML هیچ اتفاق یا مشکل خاصی پیش نمیآید اما اگر بروید روی مرورگر گزینه view source را بزنید متوجه میشود که خواندن این فایل واقعا مکافات است!
Property فوق این مشکل را برطرف میکند و خروجی HTML را طوری تولید میکند که بشود سورس آن را (مثل آدم!) خواند. فقط اشکالش اینست که در برخی Layout ها به دلیل انداختن یک سری space اضافه، صفحه را از ریخت میاندازد. در مورد سایت من چنین مشکلی پیش آمد و من بیخیال فرمتینگ شدم. Property دوم که Indentation نام دارد میزان تاثیر گذاری فرمتینگ را مشخص میکند. عدد ۲ نرمال است. اگر بیشتر بدهید، nesting سورس HTML را بیشتر میکند، خودتان امتحان کنید، متوجه خواهید شد.
سرانجام فرمان آسمانی Transform انجام میشود. همه مقاله را به خاطر این تابع پرتشریفات نوشتم. میخواستم از اول بگویم این تابع چه تابع خوبی است، توی رودربایستی افتادم و این مقاله را نوشتم!
فراموش نکنید که مجرای XmlTextWriter را باید پس از اتمام کار بست. حالا اگر به پوشهای که میخواستید فایلتان آنجا ذخیره شود مراجعه کنید میبینید که یک صفحه وب تر و تمیز حاوی مقاله یا خبر شما آنجا نشسته و اسم دلخواه شما را نیز به خود گرفته است (در اینجا همان کد id).
باید datagrid خودتان را طوری بسازید که بشود از طریق یک checkbox کنار هر رکورد، آنهایی که مایلید صفحه خبر یا مقالهاش را بسازید، مشخص کرد. سپس یکی یکی id این رکوردها را به تابع مورد بحث میدهید تا به ازای هرکدام از آنها یک صفحه بسازد. توجه کنید که تکرار اجرای این فرمان روی یک رکورد موجب overwrite شدن فایل قبلی میشود. یعنی میتوانید به سادگی فایل را update کنید.
اگر خواستید Layout سایت تان را تغییر دهید، کافی است فایل XSL خود را یکبار دستکاری کنید. اینکار خیلی ساده است. اگر XSL را با پسوند htm یا html ذخیره کنید، برنامهای مانند Dreamweaver آن را مانند یک صفحه وب معمولی باز میکند تا بتوانید ویرایشش کنید. بعد از تغییر XSL، یکبار دیگر فرمان Transformation را روی رکورهای مقالات و اخبار تکرارکنید تا صفحات جدید را بدست آورید.
نتیجهگیری :
اگر به صورت مساله اول مقاله برگردیم، حالا متوجه میشویم این سایتهای خبری بزرگ دنیا چطوری بدون استفاده از صفحات دینامیک، سایتهای فعالی دارند. در واقع کلکش خیلی آسان بود.
salam jaleb bod!be hamin rahaty ke fek mikoni nist in ravesh pish pa oftadas yani raveshi nist ke bbc ya cnn estefadeh mikoneh ye rozi 5 ta developere vaghean expert bodim mikhastim chizi be esme pixgen benevisim ke hamin karo mikard albateh az nazare kari ye chiz dar hade joomla khob nashod mamnon az maghalat!