HaskellでのXMLパース処理 - XPath使用
今回は、id:fits:20100525 で実施したパース処理を Haskell で書いてみました。
環境は以下の通りで、XML の処理のために HaXml というライブラリをインストールしました。
- GHC 6.12.1
- HaXml 1.20.2
Haskell での XPath
HaXml では、xmlParse で XML をパース、xtract で XPath を処理し、tagTextContent で要素内のテキストを取得できます。
tagTextContent の代わりに Text.XML.HaXml.Pretty の content を使えば要素全体を取得できます。
なお、xtract の型宣言が xtract :: (String -> String) -> String -> Content i -> [Content i] となっているため、(String -> String) の箇所に、引数の値をそのまま返す id 関数をあてがっています。
xbrl_parse.hs
import System import Text.XML.HaXml import Text.XML.HaXml.Posn import Text.XML.HaXml.Util import Text.XML.HaXml.Xtract.Parse main = do args <- getArgs contents <- readFile $ head args let -- XML のパース -- データコンストラクタを使ったパターンマッチで Element 取得 Document _ _ root _ = xmlParse "" contents -- Content 取得 cont = CElem root noPos mapM_ (print . tagTextContent) $ xtract id "//jpfr-t-cte:OperatingIncome[@contextRef='CurrentYearConsolidatedDuration']" cont -- 以下でも可 -- let Elem n _ cont = root -- mapM_ (print . tagTextContent) $ concatMap (xtract id "//jpfr-t-cte:OperatingIncome[@contextRef='CurrentYearConsolidatedDuration']") cont
型が分かり易くなるように、関数を使って書き直すと以下のようになります。
xbrl_parse2.hs
import System import Text.XML.HaXml import Text.XML.HaXml.Posn import Text.XML.HaXml.Util import Text.XML.HaXml.Xtract.Parse -- XPath で検索 findNodeList :: String -> Content Posn -> [Content Posn] findNodeList pattern ct = xtract id pattern ct -- Element を Content へ toContent :: Element Posn -> Content Posn toContent el = CElem el noPos -- XMLのパース parseXmlString :: String -> Element Posn parseXmlString ct = let Document _ _ root _ = xmlParse "" ct in root main = do args <- getArgs contents <- readFile $ head args let nodeList = findNodeList "//jpfr-t-cte:OperatingIncome[@contextRef='CurrentYearConsolidatedDuration']" (toContent $ parseXmlString contents) mapM_ (print . tagTextContent) nodeList
XPath を使わない方法
XPath を使うよりは冗長になってしまいますが、deep, tag, attrval を使う事で同等の処理が可能です。
xbrl_parse3.hs
import System import Text.XML.HaXml import Text.XML.HaXml.Posn import Text.XML.HaXml.Util main = do args <- getArgs contents <- readFile $ head args let Document _ _ root _ = xmlParse "" contents cont = CElem root noPos mapM_ (print . tagTextContent) $ (deep $ tag "jpfr-t-cte:OperatingIncome" `with` attrval ("contextRef", AttValue [Left "CurrentYearConsolidatedDuration"])) cont