ラベル xml の投稿を表示しています。 すべての投稿を表示
ラベル xml の投稿を表示しています。 すべての投稿を表示

2011年3月23日水曜日

PHPの SimpleXMLを配列に変換する

シンプルなXMLなら、SimpleXMLElementオブジェクトをarrayにキャストするだけで連想配列に変換できる。

$xml = '<?xml version="1.0" ?>
<root>
<test>TEST1</test>
<test>TEST2</test>
<attr trouble="MISSING">WHY?</attr>
</root>';

$sx = simplexml_load_string($xml);

var_dump((array)$sx);
↓
array(2) {
  ["test"]=>
  array(2) {
    [0]=>
    string(5) "TEST1"
    [1]=>
    string(5) "TEST2"
  }
  ["attr"]=>
  string(4) "WHY?"
}

同じ要素名の要素がある場合(上記の例ではtest要素)、まとめて添字が0から始まる配列になる。
しかし、なぜか属性とテキストノードの両方がある要素の属性(上記の例ではattr要素のtrouble="MISSING")が無視される。

また、子要素や属性のあるXMLの場合、配列の中まで再帰的にキャストしていないのでSimpleXMLElementのままになってしまう。
$xml = '<?xml version="1.0" ?>
<root>
<parent>
<child>MUSUKO</child>
</parent>
<attr foo="FOO" bar="BAR" />
</root>';

$sx = simplexml_load_string($xml);

var_dump((array)$sx);
↓
array(2) {
  ["parent"]=>
  object(SimpleXMLElement)#3 (1) {
    ["child"]=>
    string(6) "MUSUKO"
  }
  ["attr"]=>
  object(SimpleXMLElement)#2 (1) {
    ["@attributes"]=>
    array(2) {
      ["foo"]=>
      string(3) "FOO"
      ["bar"]=>
      string(3) "BAR"
    }
  }
}




これに対して、PHPマニュアルのコメント欄に一発で配列に変換する方法が書いてあった。
$xml = '<?xml version="1.0" ?>
<root>
<parent>
<child>MUSUKO</child>
</parent>
<attr foo="FOO" bar="BAR" />
</root>';

$sx = simplexml_load_string($xml);

var_dump(json_decode(json_encode($sx), true));
↓
array(2) {
  ["parent"]=>
  array(1) {
    ["child"]=>
    string(6) "MUSUKO"
  }
  ["attr"]=>
  array(1) {
    ["@attributes"]=>
    array(2) {
      ["foo"]=>
      string(3) "FOO"
      ["bar"]=>
      string(3) "BAR"
    }
  }
}



json_decode()の第2引数(戻り値を連想配列にするかどうか)をtrueにするのがポイント。
属性は@attributesというKEYの連想配列に格納される。


しかし、属性とテキストノードの両方がある要素の属性が無視される問題は、このjson_encode/json_decodeを使った方法でも発症する。
$xml = '<?xml version="1.0" ?>
<root>
<attr trouble="MISSING">WHY?</attr>
</root>';

$sx = simplexml_load_string($xml);

var_dump(json_decode(json_encode($sx), true));
↓
array(1) {
  ["attr"]=>
  string(4) "WHY?"
}

Why?
まあでも属性値がないことが分かっているXMLならこれで使えそう。(後から属性値を使いたいということになったら大変だけど。)

きちんとやりたいなら、SimpleXMLで取得したオブジェクトを属性も含めて配列に変換 | とりさんのソフト屋さんのやり方がスマートで良さそう。

2009年11月6日金曜日

Rails 2.xで XMLサイトマップを生成する方法

Rails2.3で試した。
必要最低限の要素のみ。


config/routes.rb

map.connect "sitemap.xml", :controller => :test, :action => :sitemap


controllers/test_controller.rb
class TestController < ApplicationController
def sitemap
# サイトマップとして送信したいページのURL生成に必要な情報を取得する
@members = Member.find(:all)
@entries = Entry.find(:all)

  headers["Content-Type"] = "text/xml; charset=utf-8"

  respond_to do |format|
format.xml {render :layout => false}
end

end
end


views/test/sitemap.xml.builder
xml.instruct!
xml.urlset(:xmlns => "http://www.sitemaps.org/schemas/sitemap/0.9") do
# トップページ
xml.url do
xml.loc(url_for(:controller => :top, :only_path => false))
end
      
# 個別のページ
@members.each do |member|
xml.url do
xml.loc(url_for(:controller => :members, :action => :show, :id => member.id, :only_path => false))
end
end
・
・
・
end
「http://」から始まるフルなURLにするために「:only_path => false」を付けている。

状況によってはキャッシュさせた方が良いだろう。

検索エンジンへの通知はrobots.txt等で。(Google Webマスターツールとか使った方が安心感があるけど。)

robots.txt
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-Agent: *
# Disallow: /

Sitemap: http://www.example.com/sitemap.xml


参考:
「はじめてのRuby on Rails2」著者サポートページ
Rails2.0でGoogleサイトマップとかのsitemap.xml作る - ひげろぐ
sitemaps.org - プロトコル

2009年2月11日水曜日

SimpleXMLで子要素を取り出すいろいろな方法

PHPのSimpleXMLオブジェクトで子要素を取り出すためのいろいろな方法のメモ。

元のXMLがこんな感じの場合、

$str = <<< XML_END
<?xml version="1.0" encoding="UTF-8" ?>
<root>

 <parent>

  <child>aaa</child>
  <child>bbb</child>
  <child>ccc</child>
  <count>3</count>
 </parent>
 <hy-phen>hyphen</hy-phen>
</root>

XML_END;

$xml = simplexml_load_string($str);

普通に子要素を取り出す場合
var_dump($xml->parent->count);
↓
object(SimpleXMLElement)[5] string '3' (length=1)

繰り返しの子要素を同じように取り出そうとすると、1つ目だけ取り出してしまう
var_dump($xml->parent->child);
↓
object(SimpleXMLElement)[4] string 'aaa' (length=3)

1つ目以外が欲しい場合はインデックスで指定できる
var_dump($xml->parent->child[2]);
↓
object(SimpleXMLElement)[5] string 'ccc' (length=3)

繰り返しの子要素の全てを取り出す場合、foreachが使える
foreach ($xml->parent->child as $c) {
var_dump($c);
}
↓
object(SimpleXMLElement)[3] string 'aaa' (length=3)
object(SimpleXMLElement)[6] string 'bbb' (length=3)
object(SimpleXMLElement)[3] string 'ccc' (length=3)

さらに、繰り返しの子要素だけでなく、子要素全てを取り出す場合はchildren()を使う
foreach ($xml->parent->children() as $c) {
var_dump($c);
}
↓
object(SimpleXMLElement)[4] string 'aaa' (length=3)
object(SimpleXMLElement)[3] string 'bbb' (length=3)
object(SimpleXMLElement)[4] string 'ccc' (length=3)
object(SimpleXMLElement)[3] string '3' (length=1) ← childだけでなく、countも取れている

children()を使うと、繰り返しの子要素は配列にまとめられるようだ(foreachを使う分には意識することは無いけど)
var_dump($xml->parent->children());
↓
object(SimpleXMLElement)[4]
 public 'child' => array
  0 => string 'aaa' (length=3)
  1 => string 'bbb' (length=3)
  2 => string 'ccc' (length=3)
 public 'count' => string '3' (length=1)


XML要素(タグ)の名前がPHPの変数名として使えない文字列の場合、{' '}で囲えばOK
var_dump($xml->{'hy-phen'});
↓
object(SimpleXMLElement)[5] string 'hyphen' (length=6)

NameSpaceを使っている要素の場合、children()のパラメータとしてNameSpaceを指定しないとうまく子要素が取得できない
$children = $xml->children('http://...');
このとき、$childrenのさらに子要素を取得するためには$children->children()しないとなぜかうまく取得できない。
NameSpaceの指定がその子要素に引き継がれて、そのNameSpaceに該当する子要素のみ取得するようにフィルターがかかっている?
それがchidren()をパラメータ無しで呼ぶことによってフィルターが解除されるから?


参考:PHP: SimpleXML - Manual

2008年12月6日土曜日

Googleの Sitemapにフィードを登録する場合の注意点






Google ウェブマスター ツールではSitemapとしてフィードを登録することができるが、いくつか注意点が。


1. 登録できるフィードはRSS2.0かAtom1.0のみ
 参考:
  雑記帳?一応Weblog - Atom 1.0キボン
  サイトマップの作成 - ウェブマスター向けヘルプ センター


2. Atom1.0を登録する場合、各エントリーにpublished(作成日)要素が必要
 publishedが無いとエラーになる。
 Atom1.0の仕様としてはupdatedは必須だが、publishedは必須ではないのだが。
 参考:
  Error in parsing sitemap for Atom Feed - Invalid XML tag - Sitemap Protocol | Google グループ
  RFC 4287 The Atom Syndication Format 日本語訳 - futomi's CGI Cafe (Atom1.0仕様書の和訳)

(2008/1/7 追記)
いつの間にか、Atom1.0でエントリーにpublishedが無くてもエラーにならなくなっていた。

2008年10月30日木曜日

Scala調査メモ

Javaから離れて久しいが、最近Scalaが気になる。



感想:
  • 文法が面白い。ループとか。
  • XMLを練り込めるのが面白い。
  • 特にレンタルサーバの場合など、PHPほど気軽に使えなさそう。
  • Apacheのモジュールで動けば気軽に使えるのだが...

2007年12月16日日曜日

RSS/AtomフィードのXMLの作り方メモ

とりあえず、必要最小限の要素だけを作る方法のメモ。


RSS1.0
シンプルなRSSの全体に、Dublin Coreモジュールのdateを加える。
各itemのdescriptionにはエスケープしたhtmlを入れられる。

<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/"
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xml:lang="ja">

 <channel rdf:about="http://example.com/rss1.rdf">
  <title>RSS1.0 Test</title>
  <link>http://example.com</link>
  <description>RSS1.0のテスト</description>
  <items>
   <rdf:Seq>
    <rdf:li rdf:resource="http://example.com/page1"/>
    <rdf:li rdf:resource="http://example.com/page2"/>
   </rdf:Seq>
  </items>
 </channel>

 <item rdf:about="http://example.com/page1">
  <title>ページ1</title>
  <link>http://example.com/page1</link>
  <description>1つ目のページ</description>
  <dc:date>2007-01-01T00:00:00+09:00</dc:date>
 </item>

 <item rdf:about="http://example.com/page2">
  <title>ページ2</title>
  <link>http://example.com/page2</link>
  <description>2つ目のページ</description>
  <dc:date>2007-01-02T03:04:05+09:00</dc:date>
 </item>
</rdf:RDF>


RSS2.0
▽PHP4でRSS配信プログラム xmlファイルの作成からlastBuildDateを削除し、各itemにdescriptionを追加する。
(lastBuildDateはあったほうがよい?)
docs要素はなくてもいいが、せっかくなので付けておく?
他のフィードと違い、日付はRFC2822形式(例:Sun, 05 Nov 2006 16:53:33 +0900)。
フィード自体のURLを示す箇所が無い。
各itemのdescriptionにはエスケープしたhtmlを入れられる。
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
 <channel>

  <title>RSS2.0 Test</title>
  <link>http://example.com</link>
  <description>RSS2.0のテスト</description>
  <language>ja</language>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>

  <item>
   <title>ページ1</title>
   <link>http://example.com/page1</link>
   <description>1つ目のページ</description>
   <pubDate>Mon, 01 Jan 2007 00:00:00 +0900</pubDate>
  </item>

  <item>
   <title>ページ2</title>
   <link>http://example.com/page2</link>
   <description>2つ目のページ</description>
   <pubDate>Tue, 02 Jan 2007 03:04:05 +0900</pubDate>
  </item>

 </channel>
</rss>


Atom0.3
2章 Atomフォーマット - The Atom Syndication Formatの"Atomフォーマットの構成"からフィード全体の日時(modefied)を削除し、各entryにsummaryを追加する。
xml:lang属性もfeed要素あたりに付けておく。
Atom1.0とは違い、フィード全体のidは必須ではない。
フィード自体のURLを示す箇所が無い。
各entryの2つの日時(issued、modefied)は必須。
各entryのidについてはAtom1.0と同様。
ベタなテキストでなくhtmlを入れたいなら、summaryの代わりにcontent要素を使う。
<?xml version="1.0" encoding="utf-8" ?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xml:lang="ja">

 <title>Atom feed 0.3 Test</title>
 <link rel="alternate" type="text/html" href="http://example.com"/>
 <author>
  <name>ぼく</name>
 </author>

 <entry>
  <title>ページ1</title>
  <link rel="alternate" type="text/html" href="http://example.com/page1"/>
  <id>http://example.com/page1</id>
  <modified>2007-01-02T00:00:00+09:00</modified>
  <issued>2007-01-01T00:00:00+09:00</issued>
  <summary>1つ目のページ</summary>
 </entry>

 <entry>
  <title>ページ2</title>
  <link rel="alternate" type="text/html" href="http://example.com/page2"/>
  <id>http://example.com/page2</id>
  <modified>2007-01-02T03:04:05+09:00</modified>
  <issued>2007-01-02T03:04:05+09:00</issued>
  <summary>2つ目のページ</summary>
 </entry>

</feed>



Atom1.0
簡単なAtomフィード文書を元にする。
xml:lang属性もfeed要素あたりに付けておく。
feedおよび各entryのid要素は"永久的で完全に一意的な識別子"なので、手軽に済ますならfeedやentryを一意に示すURLでよいと思う。
(パーマリンクは一意と言えないという意見もある。それが理由かは分からないがAtomの基本的なフィードの例ではurlのtagスキームを、簡単なAtomフィード文書のサンプルではurnを使っている。)
feed全体のupdated日時が必須。
linkでrel属性を指定しないとrel="alternate"とみなされる。
summaryとcontentについてはAtom0.3と同様。
フィードリーダーによってはAtom1.0は対応していないので注意が必要。(他のフィードと併用するなど。)
<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">

 <title>Atom feed 1.0 Test</title>
 <link href="http://example.com"/>
 <link rel="self" href="http://example.com/atom1.xml"/>
 <author>
  <name>ぼく</name>
 </author>
 <updated>2007-12-12T14:20:37+09:00</updated>
 <id>http://example.com/atom1.xml</id>

 <entry>
  <title>ページ1</title>
  <link href="http://example.com/page1"/>
  <summary>1つ目のページ</summary>
  <updated>2007-01-01T00:00:00+09:00</updated>
  <id>http://example.com/page1</id>
 </entry>

 <entry>
  <title>ページ2</title>
  <link href="http://example.com/page2"/>
  <summary>2つ目のページ</summary>
  <updated>2007-01-02T03:04:05+09:00</updated>
  <id>http://example.com/page2</id>
 </entry>

</feed>



補足:日付のタイムゾーンについて
フィードリーダーによっては、タイムゾーンが正しく読めない? UTC (例:2005-11-16T02:27Z)で配信する?


関連記事: RSS/Atomフィードを生成したりSitemapを作成するPHPライブラリ


参考:
RSS1.0
 RSS(RDF Site Summary)によるサイト情報の要約と公開 (RSS1.0についての解説)
 RDF Site Summary (RSS) 1.0 (RSS1.0仕様書の和訳)
RSS2.0
 RSS 2.0 Notes (RSS2.0仕様書を元にした解説)
 RSS 2.0 Specification 日本語訳 - futomi's CGI Cafe (RSS2.0仕様書の和訳)
 ホームページ作成 / Yaskey Diary (RSS2.0のシンプルな例)
Atom0.3
 2章 Atomフォーマット - The Atom Syndication Format (Atom0.3仕様書の和訳)
Atom1.0
 Atom - RSS改訂の試み (Atom1.0についての解説)
 RFC 4287 The Atom Syndication Format 日本語訳 - futomi's CGI Cafe (Atom1.0仕様書の和訳)
 An overview of the Atom 1.0 Syndication Format (Atom1.0の解説)
その他
 Web標準の日時の形式 | Web::Blogoscope (日付形式について)
 RSS1.0とRSS2.0 - fox.tech - by fox_b (RSS仕様の分裂について)

ブログ アーカイブ

tags