トップ   差分 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS

XMLに手を出した

おぼえがき・その2


今更だけど.Net Framework2.0
モバイルWEBフォームとかあるのに不便。


ケータイでみれないぉ!(・∀・)

自分でサイト作るとこういうとこがもう…ね…

フィルター(この場合スキンやテンプレートみたいなイメージ)とやらで一発変換できないのけ!

とかまず思う。いろいろ調べてみたけど…ないな。そんな便利機能。
便利ツールは自分で実装しなきゃいけないみたいだ。

まずは出力をShift-Jisにしないとな…
streamとかでshift-jisで吐き出すとか手はあるだろうけど
それならweb.configで設定して…結局別仮想ディレクトリかいな。
ということで昔懐かしいUserAgent?で判別ロジックを組んでPCサイトからリダイレクトを実装。
ケータイ用サイトに飛ぶようにする。


でもこれじゃぁケータイ用サイトをまた作るって手間になるなぁ。
便利ツール(頭の中にだけあるケータイ向け便利スクリプト)を
既存masterファイルに噛ませてなんとかならないかな〜。

…とか考えるときっとこうなる。

<%if%>
  PC用
<%else%>
  ケータイ用
<%endif%>

…きっとこれじゃ(マイクロソフトのvisualなんたら製品で)デザイナ画面が見れない。
(私的サイトなんだし(?)デザイナ見ながら作業したいよ。)


ケータイでみれないぉ!(・∀・)その2

便利ツール(頭の中にだけあるケータイ向け便利スクリプト)を考えてみよう。
UserAgent?判別。
・おそらくケータイ向けテンプレ(masterでもいいしテキストベースでもいい)を読み込む
・モバイルWEBページにリダイレクトとかもあり?
・既存(PC向け)コンテンツがUTF-8なためShift-JisによるStreamWriter?必須。

テンプレは…ハッ(゚д゚)XSLでいいのか?!



便利でもないツールをつくってみた。

参考はココ
流れとしては

トップページ.aspxでUserAgent判別
↓          ↓
(PC)        (ケータイ)
そのまま表示    ケータイ.aspx(便利ツールのつもり)へリダイレクト
           ↓
          テキストファイル(XML)を読み込んでHTMLへ変換(XSL)してShiftJisで出力



諸事情によりコードファイルで実現できないので共通っぽいのは
懐かしいIncludeで読み込ませる。
トップページ.aspx

<%@ Page Language="C#" ・・・%>

<script runat="server">
    public string mobfile = "ケータイ.aspx"; 
</script>
<!--#include file="判別用スクリプト.inc"-->

<!DOCTYPE ・・・・>
<html ・・・>

とりあえずトップページだけでも対応するってことで
判別用スクリプト内でケータイ.aspxへリダイレクト。
判別用スクリプトはこんだけ。IT@MEDIAの記事を参考。(いらないコードも入ってるな…)

<script runat="server">
    private readonly static string[] MobileUserAgentList = new string[]{
        "(DoCoMo){1}",
        "(UP.Browser){1}",
        "(J-PHONE){1}",
        "(Vodafone){1}",
        "(SoftBank){1}"
    };

    protected bool isMobile(string[] agentInfo)
    {
        foreach (string usr in agentInfo)
        {
            foreach (string exp in MobileUserAgentList)
            {
                if (Regex.IsMatch(usr, exp))
                {
                    return true;
                }
            }
        }
        return false;
    }

    protected new void Page_Load(object sender, System.EventArgs e)
    {
        string[] agentInfo = HttpContext.Current.Request.UserAgent.Split('/');
        if (agentInfo.Length == 0)
        {

        }
        else
        {
            if (isMobile(agentInfo))
            {
                Server.Transfer(mobfile);
            }
        }
    }
</script>

作りやすいのでこの判別スクリプトもaspxファイルにしてたけど
mobfile(トップページ.aspxで指定したケータイ用ファイル)のスコープエラーとか
PageLoad?はnewいらないとかいるとかいろいろ出るので.incとかにしてみた。
ウチのマシン性能のせいかもしれんケドね…デバッグ情報が少し遅い。
PageLoad? にnewが必要なのはコードファイル側にすでにPageLoad?があったからっぽい…。

そしていよいよ!
のちのちこれが便利ツール本体になるはずの!
ケータイ.aspx

<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Xml.Xsl" %>
<%@ Import Namespace="System.Xml.XPath" %>
<script runat="server">
    //------------------------------------------------
    //PageLoadでなくPage_PreRenderってところがミソらしい
    //------------------------------------------------
    protected void Page_PreRender(object sender, System.EventArgs e)
    {
        //--------------------------
        //XML(テキスト)をXSLでhtmlに変換
        //--------------------------
        // ファイルを取得するには相対パスじゃだめらしい。
        string xfpath = Context.Server.MapPath("~/データディレクトリ");
        string xspath = Context.Server.MapPath("~/xslが置いてあるディレクトリ");
        string xmlfile = xfpath + "/データ.xml";
        string xslfile = xspath + "/スタイル.xsl";
        
        //XMLを読み込む
        XmlDocument myXMLDoc = new XmlDocument();
        myXMLDoc.Load(xmlfile);
        //XSLTを変換用に読み込む
        XslCompiledTransform transformer = new XslCompiledTransform();
        transformer.Load(xslfile);
        
        //読み込んだXMLのリーダーを生成
        XmlNodeReader reader = new XmlNodeReader(myXMLDoc);
        //変換するためのメモリ領域生成
        MemoryStream xs = new MemoryStream();
        //変換するためのライター生成
        // * ちなみにxmlもxslもShift-Jisで作ったが
        // * ここのエンコードはデフォ(UniCode?UTF-8?で。)
        XmlTextWriter xw = new XmlTextWriter(xs, null);
        
        //いざ!変換。リーダーをライターへ。
        transformer.Transform(reader, xw);
        
        //------------------------
        //Responseするための準備
        //------------------------
        //変換した中身を読み込む
        xs.Seek(0, SeekOrigin.Begin);
        StreamReader xr = new StreamReader(xs);
        string outXhtm = xr.ReadToEnd();
        xs.Dispose();
        
        //Reasponseライター生成。ここでエンコードをShift−Jisへ。
        Encoding enc = Encoding.GetEncoding("shift_jis");
        Stream stream = Response.OutputStream;
        StreamWriter writer = new System.IO.StreamWriter(stream, enc);
        writer.Write(outXhtm);
        writer.Flush();
        
    }
</script>

中身はこんだけ。
XML→XSL変換でShift-Jisにしたら二重変換みたいになった。
…データファイルやなんやはデフォで作ってもいいのかもしれんな…。
↑コード中のコメントに「xslもshift-jis」と書いてあるがutf-8のものもあった…
↑…xmlはshift-jisだったり(´・ω・)混在モード…
↑それで文字化けしてたのかもなぁ。最後のwriterがshift-jisで出してるから問題ナイっぽい。



リダイレクト無しで・・・

というわけで、記事部分をXMLで、XSLはHTMLにするために通す。
トップページの記事xmlとxslを設定。
ケータイ.aspx→ケータイ.incにして<%@ Page ・・・%>を消す。
トップページ.aspx

<%@ Page ・・・ %>
<script runat="server">
    public string myXmlpath = "~/データディレクトリ";
    public string myXslpath = "~/xslの置いてあるディレクトリ";
    public string myXml = "/記事.xml";
    public string myXsl = "/ケータイ用変換.xsl";
</script>
<!--#include file="判別用スクリプト.inc"-->
<!--#include file="ケータイ.inc"-->

<!DOCTYPE html ・・・>

<html xmlns="http://www.w3.org/1999/xhtml" >
:
(以下通常ページの記述)


ケータイ.incのPage_PreRender?をシリアル化…(?というんだろうか)
↓リダイレクト無しの場合、Page_Loadでケータイ判別〜その後処理〜レスポンスで終了。
↓つまりケータイの時はPage_Loadで終了させたい。
Page_PreRrender?はwebアプリがページを表示する時のイベントなのでオリジナルのメソッドにする。
(つまり通常のイベントと被らないようにメソッド名を変更。この際なんでもいい。)

     protected void myPage_PreRender(object sender, System.EventArgs e)
     {
       :

ファイルパス取得のところを書き換え。
ケータイ.inc読み込み前にセットしてある変数を参照る。

        //--------------------------
        //XML(テキスト)をXSLでhtmlに変換
        //--------------------------
        // ファイルを取得するには相対パスじゃだめらしい
        string xfpath = Context.Server.MapPath(myXmlpath);
        string xspath = Context.Server.MapPath(myXslpath);
        string xmlfile = xfpath + myXml;
        string xslfile = xspath + myXsl;


ケータイ.incのwriter.Flush()の後にレスポンス用の処理コードを追加。
↓これでケータイの時はPage_Loadイベントでhttpレスポンス終了になる。

        this.Response.HeaderEncoding = enc;
        this.Response.ContentType = "text/html; charset=Shift_JIS";
        this.Response.Flush();
        this.Response.End();

レスポンス終了!
ContentType?のcharsetがShift_JISが正しいのかShift-JISが正しいのか…
これでトップページ.aspxに書かれているWEB.UIのレスポンスを抑止。


判別用スクリプト.incのリダイレクトをmyPage_PreRender?呼び出しに書き換え。

    protected new void Page_Load(object sender, System.EventArgs e)
    {
        :
             if (isMobile(agentInfo))
             {
                 //Server.Transfer(mobfile);
                 myPage_PreRender(sender, e);
             }
        :
    }


…いちおできたけど… 記事.xmlのバッファ長とか改良点はありそうなかんじ(′・ω・`)


Masterファイルを使っていたので・・・

aspxの各ページはMasterファイル使ってデザインを統一している。
デザインの基本は:
・レイアウトはMasterに書いたとおり
・色、背景画像はPage_LoadでMasterにcssを追加する形で実現

…Page_Load(´・ω・)
いざ携帯に変換しようとしたら判別スクリプト(判別.inc)はPage_Loadに書かれていた…。
ので判別はPage_PreRender?イベントで行うようにした。

<script runat="server">
 : 
    protected new void Page_PreRender(object sender, System.EventArgs e)
    {
        string[] agentInfo = HttpContext.Current.Request.UserAgent.Split('/');
        if (agentInfo.Length == 0)
        {

        }
        else
        {
            if (isMobile(agentInfo))
            {
                 myPage_PreRender(sender, e);
            }
        }
    }
</script>

…元々Page_PreRender?でやるべきところだったんだもんな〜
ってコトで問題なし(・∀・)…のハズ。
↑newが付いてるのは…うpするサーバーではdllとか使えないんだけどローカルではコンパイルできるから〜。どちでも使えるようにしてみただけ。


Masterファイルを使っていたので・・・(その2)

MasterType?使ってmasterにオーバーライドしてるaspxで巧く変換されないぉ!(・∀・)
…ってことでmasterにケータイ用スクリプト埋めることにする…
↑試していたファイルパス不正でtry〜catchしたのにthrowしてなかっただけかもだが今となってはわからん…
masterにケータイ変換用変数のget/setアクセサを実装…

    protected string myXmlpath = "~/データディレクトリ";
    protected string myXslpath = "~/xslの置いてあるディレクトリ";
    protected string myXml = "/記事.xml";
    protected string myXsl = "/ケータイ用変換.xsl";
    public string MobXmlPath
    {
         get{return myXmlpath;}
         set{myXmlpath = value;}
    }
        :
      (以下同じようにオナジ用に宣言)

…面倒なのでこれも別ファイル化。(ケータイ用変数.inc) masterにinclude

<%@ Master ・・・ %>
<%@ Import namespase="System.Web.UI.WebControls" %>
      :
    (import部分)
      :
<script runat="server">
    // Masterのweb.ui.contorolに対して上書きするためのアクセサ
    public string テキストラベル
    {
           get { return this.Label1.Text; }
           set { this.Label1.Text = value; }
    }
        :
</script>
<!--#include file="ケータイ用変数.inc"-->
<!--#include file="判別用スクリプト.inc"-->
<!--#include file="ケータイ.inc"-->
<script runat="server">
    // MasterのPage_Load処理(通常=PC表示時の処理)
    protected new void Page_Load(object sender, System.EventArgs e)
    {
        :
    }
</script>

<!DOCTYPE html ・・・>

<html xmlns="http://www.w3.org/1999/xhtml" >
:
(以下通常masterページの記述)

このmasterを使うaspxの記述

 <%@ Page … %>
 <%@ MasterType … %> 
 <script runat="server">
 protected new void Page_Load(object sender, System.EventArgs e)
 {
     Master.MobXmlpath = "~/App_Data/x_heavnsdoor";
     Master.MobXslpath = "~/x_heavnsdoor/css";
     Master.MobXml = "/description.xml";
     Master.MobXsl = "/m_x_heavns.xsl";
     // 通常(=PC)用の変数もセット
     Master.テキストラベル = "PC用の変えたいテキストはココ";
        :
 }
</script>
:
(以下通常ページの記述)
 

これでなんかスッキリ(・∀・)
だけどイベント処理的にこれでいいのかな〜(´・ω・)とか思ってしまう
aspxの方がPage_Loadでmasterの方がPage_PreRender?っていう。
MSDNで見たらイベントパイプラインではPreRender?の方が後。
aspxに書いたモノがmasterをオーバーライドできるってことは

aspxのLoad→masterのLoad→ 

なんじゃないかと。しかしこの先は…(´・ω・)
コンテナの記述順でいえばmaster→aspxみたいなのでPreRender?はmasterの方が先っぽいような…(アヤシイ)

(続く)

EOF


Last-modified: 2009-05-30 (土) 21:33:37 (5447d)