【基盤構築】XML::SimpleとDOM(XML読み込み部品②) | 自転車旅行とWebサービス

自転車旅行とWebサービス

しがないSEが自転車旅行にかまけながら自前Webサービス作成を目指す

 

 

前回、こんなふうなXML↓

 

<?xml version="1.0" encoding="UTF-8"?>
<DCMN>
  <DataBase>
    <DataSource>DBI:mysql:sato:127.0.0.1:3306</DataSource>
    <DataBase>sato</DataBase>
    <Pass>sato</Pass>
  </DataBase>
</DCMN>

 

をDOMで読み込むという方針にした。

まずはなんも考えんと早速実装する。


#!"C:\Perl64\bin\perl.exe"


use CGI::Carp qw(fatalsToBrowser);
use XML::Simple;


my $conf = XMLin('./CMN/CMN_config.xml');


print "Content-Type: text/html; charset=UTF-8\n\n";


print '<html>';
print '<head>';
print '<meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS">';
print '<title>TEST</title>';
print '</head>';
print '<body>';


print 'DataSource : '.$conf->{DataBase}->{DataSource};
print '<br>';

print 'DataBase : '.$conf->{DataBase}->{DataBase};
print '<br>';

print 'Pass : '.$conf->{DataBase}->{Pass};


print '</body>';
print '</html>';

 

何にも考えてないので何も難しくない。
my $conf = XMLin('./CMN/CMN_config.xml');
でカレントディレクトリから目的のXmlファイルの場所を指定してインスタンスを取得。


XMLの構造がそのままハッシュの入れ子になっているので

$conf->{DataBase}->{DataSource};

で取得。以上

 
  
 

 

 

これを共通部品化して以下のようにまとめる。


cgi-bin
│  (メインプログラム)testXML.cgi

└─CMN
       (設定ファイル)CMN_config.xml
       (共通部品)CMN.pm

 

XML読み込みはどのプログラムからも必要とされそうな感じがするので
CMN直下にする。


このまえみたいにモジュールを作成する。
そんなモジュールを呼び出すメインプログラムtestXML.cgiはこう↓


#!"C:\Perl64\bin\perl.exe"

use CGI::Carp qw(fatalsToBrowser);
use lib './CMN';
use CMN::CMN;

 

print "Content-Type: text/html; charset=UTF-8\n\n";


print '<html>';
print '<head>';
print '<meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS">';
print '<title>TEST</title>';
print '</head>';
print '<body>';

my $element = CMN::CMN->CMN_getXmlElement(Xml => './CMN/CMN_config.xml', Node => 'DataBase/DataSource');
print 'DataSource : '.$element.'<br>';

my $element = CMN::CMN->CMN_getXmlElement(Xml => './CMN/CMN_config.xml', Node => 'DataBase/DataBase');
print 'DataBase : '.$element.'<br>';

my $element = CMN::CMN->CMN_getXmlElement(Xml => './CMN/CMN_config.xml', Node => 'DataBase/Pass');
print 'Pass : '.$element.'<br>';


print '</body>';
print '</html>';


今回作った共通部品が以前と異なるのは引数が要ることである。
たったそれだけである。
たったそれだのことが大変だった・・・


キモはここだけ
my $element = CMN::CMN->CMN_getXmlElement(Xml => './CMN/CMN_config.xml', Node => 'DataBase/Pass');
引数はXmlのファイルの場所と目的の要素までのノードのみ
これで目的の要素の値を返してきてもらう。


DOMのくせに毎回インスタンス化すんのかよ・・・と思うかもしれないが
いつかはSAXにするつもりなのでこうした。


ちなみにPerlではこんなふうな引数の渡し方を名前付き引数と呼ぶらしい
他の言語のように
my $element = CMN::CMN->CMN_getXmlElement('./CMN/CMN_config.xml', 'DataBase/Pass');
じゃだめなのか?と思う。
思うがだめなのだ


何がダメなのかというと受け取り側でダメになってしまうのである。

 不可能なわけではない

 

 

そんな受け取り側のCMN.pmはこんな↓

#!"C:\Perl64\bin\perl.exe"

package CMN::CMN;
use XML::Simple;

sub new {
    return bless {} ;
}

sub CMN_getXmlElement{

    my $class = shift;
    my %args = (Xml => '', Node => '', @_);
    my $args = \%args;

    return getDomElement(Xml => $args{Xml}, Node => $args{Node});
}


sub getDomElement{
    my %args = (Xml => '', Node => '', @_);
    my $args = \%args;

    my @Nodes = split(/\//, $args{Node});

    my $Xml = XMLin("$args{Xml}");

    foreach my $tmp (@Nodes){
        $Xml = $Xml->{"$tmp"};
    }

    return $Xml;
}

1;


どこで引数を受け取るのか?
こう↓じゃないのかと思うだろう?
sub CMN_getXmlElement(Xml, Node){
俺もそう思った


でも実際はそう書いてない。
どこに引数受取が書いてあるかというと
    my $class = shift;
    my %args = (Xml => '', Node => '', @_);
    my $args = \%args;
この3行である。
3行も使ってしまうのである。


しかも引数を受け取ってはいない。
具体的にはハッシュの値を引数で上書きしているが正しい。
本当の引数はどこにあるのか?
正解はここ↓
 @_
この特殊変数@_に引数+αが配列で入っている。


+αとはなにか?
なんとPerlは外部からパッケージとして呼び出すと、引数が増えるのである。
増えた邪魔な部分をとるのが
    my $class = shift;
である。
shiftで@_からキューのように最初の値をとるのだ。

んでとった後の$classは使わない。


さっき、「外部から」と書いたが、
同一パッケージ内部から呼び出された場合は引数は増えない。
従ってこのCMN_getXmlElementは内部から呼ばれると正しく動かない


どんな仕様やねんこの言語・・・・


しかも受け取るのはハッシュでなくハッシュのレファレンスだし・・・

 別に、ハッシュにしなくても、@_からshiftで次々と取り出すことができるが、
何が入ってくるかわからないし、そんなスクリプト臭すぎるのはいやだし、
そもそもPerlは引数の型や数でエラーにならないからおかしな値がきてもわからない。

そんなわけで1か月後にプログラム修正なんてしたら
頭がおかしくなって死ぬ可能性があるからやめるべきである。





 

愚痴はそのへんにしておいて、
大まかな流れは、


PG呼び出し→CMN_getXmlElement(Xml, Node)→getDomElement(Xml, Node)
                                              ↓
取得した値←CMN_getXmlElement(Xml, Node)←getDomElement(Xml, Node)


という流れである。
以前言った通り、いつかはSAXにサッと切り替える予定のためCMN_getXmlElementは処理をせず次のメソッドに処理を投げるだけ。
getDomElement(Xml, Node)が処理を行う。


getDomElement(Xml, Node)は取得したXmlのオブジェクトに対し、走査を行う。
走査を行うノードは"/"繋がりで送られてくると決め、
    my @Nodes = split(/\//, $args{Node});
"/"で配列に分割して次のforeachで走査する。



JavaやVBではSpritは特定「文字」で分割していたが、
PerlのSpritは特定「パターン」で分割する。
だから"/"で挟んで/\//となる。


変なとこで気が利いてるよね。

  

結果画像はどうせ同じだから省略する。






さて、これをDCMN.pmに組み込む


#!"C:\Perl64\bin\perl.exe"

package CMN::DCMN::DCMN;
use lib './CMN';
use CMN::CMN;
use DBI;

our $XmlPath = './CMN/CMN_config.xml';


sub new {
    return bless {} ;
}

sub DCMN_connect{

    $Xml = CMN::CMN->new();

    $dbh = DBI->connect(
                         $Xml->CMN_getXmlElement(Xml => $XmlPath, Node => 'DataBase/DataSource')
                        ,$Xml->CMN_getXmlElement(Xml => $XmlPath, Node => 'DataBase/DataBase')
                        ,$Xml->CMN_getXmlElement(Xml => $XmlPath, Node => 'DataBase/Pass')
                        );

    return $dbh;
}

1;


あ・・・なんかプログラムの上のほうが汚くなってきた・・・まあいいか

 


というわけでちょっとした共通部品がようやく完成。
今のフォルダ構成

cgi-bin
│  test.cgi
│  testDBI.cgi
│  testDBI2.cgi
│  testDBI3.cgi
│  testDBI4.cgi
│  testXML.cgi
│  testXML2.cgi
│ 
└─CMN
    │  CMN.pm
    │  CMN_config.xml
    │
    └─DCMN
            DCMN.pm

 

テストシリーズばっかりやないか!!

 


さて次は画面!


みんな大好きフロントサイド!