日別アーカイブ: 2015年8月23日

2件の投稿

XLSTを使って整形

取り敢えず下のURLをクリックしてください。

https://hi-kaka.com/tmp/chibaTenki.xml

前回ご紹介した、気象庁のデータをダウンロードし、下に示すXSLTファイルを関連づけました。ブラウザはchibaTenki.xmlファイルをXSLTで整形して表示しています。

chibaTenki.xmlの修正は、2行目に次の文を追加しただけです。

<?xml-stylesheet type=”text/xsl” href=”./chibaTenki.xslt”?>

ブラウザだけでなく、大抵の言語、Php、Java、Javascript、Perl、.NETで、XMLをXLST変換して新たなXMLを作成することができます。

ここで、chibaTenki.xsltが問題のXSLTファイルです。

久しぶりに手探りでXSLTを作成したし、決して立派なものではありません。XSLTをご存知ない方に、「XSLTとはこんなものです」というくらいのサンプルです。

コードを簡単にご説明します。

<xsl:template match=”/weatherforecast/pref”>(8行目)では、XMLドキュメントの”/weatherforecast/pref”タグを探して、あったらprefタグの処理をします。

ここでしていることは、出力をHTMLタグで囲んで(9行目から16行目)、HTMLファイルのタイトルを「千葉県の天気」とし、出力bodyタグの中、<xsl:apply-templates select=”area”/>(14行目)で、「以下は<xsl:template match=”area”>に任せる」としています。

制御は<xsl:template match=”area”>(18行目)に移り、18から31行目の中でareaタグの処理をします。

続いて制御が<xsl:template match=”info”>(33行目)に移り、必要な処理をした後に、<xsl:call-template name=”RAIN”/>(58行目)でサブルーチン・テンプレートをコール、<xsl:template name=”RAIN”>(61行目)の中で、rainfallchanceタグの処理をします。

<?xml version="1.0" encoding="Shift_JIS"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="html" indent="yes"/>
 <xsl:variable name="prefecture" select="/weatherforecast/pref/@id"/>
 <xsl:template match="/weatherforecast/title|link|description|pubDate|author|managingEditor|">
  <xsl:text/>
 </xsl:template>
 <xsl:template match="/weatherforecast/pref">
  <html>
   <head>
    <title>千葉県の天気</title>
   </head>
   <body>
    <xsl:apply-templates select="area"/>
   </body>
  </html>
 </xsl:template>
 <xsl:template match="area">
  <table border="1" width="700">
   <tbody>
    <tr>
     <th>
      <xsl:value-of select="$prefecture"/>
      <xsl:text>   </xsl:text>
      <xsl:value-of select="@id"/>
      <xsl:apply-templates select="info" />
     </th>
    </tr>
   </tbody>
  </table>
 </xsl:template>
 
 <xsl:template match="info">
  <tr>
   <th>日時 <xsl:value-of select="@date"/>
   </th>
  </tr>
  <tr>   <td/>
   <td>
    <xsl:value-of select="weather"/>
   </td>
  </tr>
  <tr>   <td/>
   <td>    <xsl:value-of select="weather_detail"/>
   </td>
  </tr>
  <tr>   <td/>
   <td>    <table>     <tbody>
      <tr>       <td>        <xsl:text>気温  </xsl:text>       </td>
       <td>
        <xsl:value-of select="temperature/range[@centigrade='min']"/>
        <xsl:text> から </xsl:text>
        <xsl:value-of select="temperature/range[@centigrade='max']"/>
       </td>      
    </tr>
   </tbody>    </table>   </td>
  </tr>
  <xsl:call-template name="RAIN"/>
 </xsl:template>
 
 <xsl:template name="RAIN">
  <tr>   <td/>
   <td>    
  <table>     <tbody>
      <tr>       <td>
        <xsl:text>降水率  </xsl:text>
       </td>      </tr>
      <xsl:call-template name="RAINBYHOUR"/>
    </tbody>    </table>
   </td>  </tr>
 </xsl:template>
 <xsl:template name="RAINBYHOUR">
  <xsl:for-each select="rainfallchance/period">
   <xsl:sort select="@hour"/>
   <tr>    <td>
     <xsl:text>  </xsl:text>
     <xsl:value-of select="@hour"/>
     <xsl:text>  </xsl:text>
     <xsl:value-of select="."/>
     <xsl:text> % </xsl:text>
    </td>   </tr>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

余談ですが、私は10年以上前に[XML Spy]というソフトを購入しました。
「使用権は永久」ということで安心していたのですが、今回これを引っ張り出して使おうとするとうまくいきません。

Altova社にメールしたら、私のライセンスは一度更新していて、最新の(といっても10年前の)ライセンス番号を送ってくれました。

というわけで、今回[XML Spy 2004]を使ってXLSTファイルを作成しました。

そんな訳で、上のXSLTファイルの2行目にstylesheet を宣言していますが、最新のXSLT2.0では、このstylesheet文を使わないことが推奨されているようです。

ここ数回に亘って、XMLについて少し整理しました。
XML処理の話はこれでおしまいです。

C#.NET XmlTextReader

前回のSAXと同じ仕事をする、XmlTextReaderを使ったコードをご紹介します。言語はC#、クラスの主要部分だけを抜き出しました。

  class MyXmlReader
  {

    private List<Area> o_lstArea = new List<Area>();

    public void XmlRead()
    {
      Area o_Area = null;
      Area.Info_data o_Info = null;

      Stack<String> elmNameStack = new Stack<String>();

      int rangCnt = 0;
      int periodCnt = 0;

      using (XmlReader reader = XmlTextReader.Create("http://www.drk7.jp/weather/xml/12.xml";))
      {
        while (reader.Read())
        {
          switch (reader.NodeType)
          {
            case XmlNodeType.Element:
              switch (reader.Name)
              {
                case "area":
                  o_Area = new Area();
                  o_Area.AreaName = reader.GetAttribute("id");

                  o_lstArea.Add(o_Area);
                  break;
                case "info":
                  o_Info = o_Area.makeInfo();
                  o_Info.Dt = reader.GetAttribute("date");
                  break;
                case "temperature":
                  rangCnt = 0;
                  break;
                case "rainfallchance":
                  periodCnt = 0;
                  break;
              }
              elmNameStack.Push(reader.Name);
              break;
            case XmlNodeType.Text:
              string elmName = elmNameStack.Pop();
              elmNameStack.Push(elmName);
              switch (elmName)
              {
                case "weather":
                  o_Info.Weather = reader.Value;
                  break;
                case "weather_detail":
                  o_Info.Weather_detail = reader.Value;
                  break;
                case "wave":
                  o_Info.Wave = reader.Value;
                  break;
                case "range":
                  o_Info.Range[rangCnt++] = Convert.ToInt32(reader.Value);
                  break;
                case "period":
                  o_Info.Rainfall[periodCnt++] = Convert.ToInt32(reader.Value);
                  break;
              }
              break;
            case XmlNodeType.EndElement:
              elmNameStack.Pop();
              break;
            }
          }
        }
      }

      public List<Area> getWeather()
      {
        return o_lstArea;
      }
    }
  }

前回と同じAreaクラスを使っていますし、やることもSAXと同じようなことをしています。

しかし、このクラスは何かのクラスを「継承していない」で、
XmlReaderを使っているだけです。

またSAXとXmlReaderの動作には大きな違いがあります。

SAXはElement(タグ)に「入った」と「出た」に対してイベントが発生するのにに対して、XmlReaderは、「Elementに入った」とか、「Textに入った」とか(他に沢山フラグが用意されています)、入った内容を知らせてきます(Elementから出たというフラグもあります)。

そして、気を付けてなければならないことはXmlReaderが読み込んで、「あ、要素だった」とか「あ、要素の内容(Text)だったか」とか分かるのですが、内容だったとき「何(タグ)の」内容なのかわからないのです。

そのために、Stackを使いました。

要素Elementに入った時に、要素名=タグ名をスタックにプッシュし、Elementから出るときにポップしています。

要素の内容(Text)が見つかった時に、スタックの一番上のタグ名を取り出し、タグ名を確認し、またプッシュしておきます。

そのほかは、前回のSAXと同じです。

XmlReaderはXMLドキュメントの特定の要素を直接読みに行く機能があるようで、もっと様々な使い方ができるのでしょうが、今の関心事は、これでおしまいです。

少なくとも、SAXの訳の分からないcharactersメソッドがないだけ、XmlReaderの方が気分的にはいいです。