Rust の XML ライブラリを探す

Rust で使える XML 操作ライブラリを探してみたら
Search Results for 'xml' - Cargo

どうやら xml-rs というのがよく使われているらしい。

An XML library in pure Rust

xml-rs - Cargo

ただ、これ DOM API が使えなくて SAX API だけ。。。

DOM があるといいのになーと思いながらも、とりあえず導入してみた。

今回の環境

Mac OS X El Capitan + Rust 1.9


$ uname -mrsv
Darwin 15.5.0 Darwin Kernel Version 15.5.0: Tue Apr 19 18:36:36 PDT 2016; root:xnu-3248.50.21~8/RELEASE_X86_64 x86_64

$ rustc --version
rustc 1.9.0

$ cargo --version
cargo 0.10.0 (10ddd7d 2016-04-08)

Cargo でプロジェクトを作成

[ヅ] Rust + Cargo で新しくプログラムを書き始める (2016-06-08) と同様に Cargo で新規プロジェクトを作成。


$ cargo new hotentry --bin

$ cd hotentry

$ tree
.
├── Cargo.toml
└── src
    └── main.rs

Cargo.toml に依存 crate を記述。

Cargo.toml に今回使うライブラリである hyper (HTTP通信ライブラリ) と xml-rs (XML操作ライブラリ) を記述。


$ cat Cargo.toml 
[package]
name = "hotentry"
version = "0.1.0"
authors = ["Nick Labadie <info@nilab.info>"]

[dependencies]
hyper = "0.9.6"
xml-rs = "0.3.4"

ソースコード

今回は、はてなブックマークはてなブックマークのホッテントリ をコンソールに出力するサンプルコードを書いた。

参考資料はこのへん。

Rust の標準ライブラリの資料。

サンプルコード。


$ cat src/main.rs

extern crate hyper; // http client
extern crate xml; // xml parser

use std::io::BufReader;

use hyper::Client;
use hyper::client::response::Response;
use hyper::error::Result;

use xml::reader::{EventReader, XmlEvent};

// いわゆるクラス的なもの
pub struct Feed {
  // フィールドに String 型の変数を持たせる
  url: String
}

impl Feed {

  // コンストラクタ
  // いわゆるクラスメソッドとして実装する
  pub fn new(url: &str) -> Feed {
    Feed{
      url: url.to_string()
    }
  }

  // フィードのエントリのタイトルとURLを取得する
  // いわゆるインスタンスメソッドは、第一引数に &self を指定する
  fn get_item_list(&self) -> (Vec<String>, Vec<String>) {

    // フィードを取得
    let feed = Feed::get_feed(self.url.as_str());

    match feed {
      Ok(v) => {
        // フィードからタイトルとURLを抜き出す
        return Feed::extract_item_list(v);
      }
      Err(e) => {
        println!("Error: {}", e);
        // エラーのときは空っぽの配列を返す
        return (Vec::new(), Vec::new());
      }
    }
  }

  // RSS フィードを取得
  fn get_feed(url: &str) -> Result<Response> {
    // hyper を使って HTTP 通信
    let client = Client::new();
    let response_builder = client.get(url);
    let result = response_builder.send();
    return result;
  }

  // RSSフィードからタイトルとURLを抜き出す
  // 動的配列 Vec が2つ入ったタプルを返す
  fn extract_item_list(res: Response) -> (Vec<String>, Vec<String>) {

    // タイトルとURLを保持する動的配列
    let mut title_list = Vec::new();
    let mut link_list  = Vec::new();

    // xml-rs を使って XML をパース
    let buf  = BufReader::new(res);
    let parser = EventReader::new(buf);

    let mut in_item_elem  = false;
    let mut in_title_elem = false;
    let mut in_link_elem = false;

    // 懐かしい XML SAX API
    // DOM API は無かった。。。
    for elem in parser {
      match elem {
        // 開始タグ
        Ok(XmlEvent::StartElement { name, .. }) => {
          match name.local_name.as_str() {
            "item"  => in_item_elem  = true,
            "title" => in_title_elem = true,
            "link"  => in_link_elem  = true,
            _ => {}
          }
        }
        // 終了タグ
        Ok(XmlEvent::EndElement { name }) => {
          match name.local_name.as_str() {
            "item"  => in_item_elem  = false,
            "title" => in_title_elem = false,
            "link"  => in_link_elem  = false,
            _ => {}
          }
        }
        // コンテンツ
        Ok(XmlEvent::Characters(text)) => {
          if in_item_elem {
            if in_title_elem {
              title_list.push(text.trim().to_string());
            } else if in_link_elem {
              link_list.push(text.trim().to_string());
            }
          }
        }
        Err(e) => {
          println!("Error: {}", e);
          break;
        }
        _ => {}
      }
    }

    return (title_list, link_list);
  }
}

fn main() {

  // はてなブックマークのホッテントリRSSフィード
  let url = "http://feeds.feedburner.com/hatena/b/hotentry";

  // struct Feed のインスタンスを生成
  let f = Feed::new(url);

  // Feed の get_item_list をコールして、
  // 動的配列の2つ入ったタプルを取得
  let (title_list, link_list) = f.get_item_list();

  // タイトルとURLを出力
  for (n, title) in title_list.iter().enumerate() {
    println!("{}\n{}\n", title, link_list[n]);
  }
}

リリースビルド

cargo build --release コマンドで、リリース用のバイナリファイルを生成することができる。


$ cargo build --release
   Compiling httparse v1.1.2
   Compiling winapi-build v0.1.1
(中略)
   Compiling cookie v0.2.4
   Compiling hyper v0.9.6
   Compiling hotentry v0.1.0 (file:///Users/hoge/hotentry)

実行ファイルは target/release 以下に配置される。


$ file ./target/release/hotentry
./target/release/hotentry: Mach-O 64-bit executable x86_64

実行結果

こんな感じでちゃんと出力できた。


$ ./target/release/hotentry 
リニア中間駅は奈良 JR東海「京都だとカーブきつい」:朝日新聞デジタル
http://www.asahi.com/articles/ASJ6865HLJ68OIPE02F.html

ブコメに/いれて違う話するの止めろ
http://anond.hatelabo.jp/20160609192302

(以下略)

tags: rust

Posted by NI-Lab. (@nilab)