Swift XMLParserでCDATAをparseをする !編集途中読まないこと!

まずCDATAとは

 XMLはマークアップ言語であるため、XML文書を記述する際には、マークアップ用として指定された記号を直接文字として記述することができない。これを記述してもマークアップの指定と解釈され、文字として解釈されないためである。 しかし、マークアップ専用の記号であって、通常の文字として扱う特例的な部分をつくることができる。これを、CDATAセクションという。

CDATAセクションは、<![CDATA[という文字列で始まり、]]>という文字列で終わる。

CDATAセクションの中にはHTMLタグも埋め込むことができるので、RSSを配信している側にとっては便利。

でもXMLパーサーを回避するので、XML解析する側にとっては嫌な存在です。 ただ、XMLParserにはCDATAを解析するメソッドが用意されている。

引用 XML用語事典 [CDATAセクション]

(初心者向け)NSXMLParserでCDATAを解析する - Qiita

中身はbitコード。

ちなみにエンドポイントはこんなかんじ。

<?xml version="1.0" encoding="UTF-8"?><![CDATA[Apple Newsroom]]><![CDATAhttps://www.apple.com/jp/newsroom/]><![CDATA[2018-11-02T02:11:47.085Z]]><![CDATA[2018-11-02T02:11:47.085Z]]><![CDATA[Apple縲∫ャャ4蝗帛濠譛溘�讌ュ邵セ逋コ陦ィ]]><![CDATAhttps://www.apple.com/jp/newsroom/2018/11/apple-reports-fourth-quarter-results/]><![CDATA[Apple Newsroom]]><![CDATA[Apple縲∫ャャ4蝗帛濠譛溘�讌ュ邵セ逋コ陦ィ
螢イ荳企ォ倥′20��い繝��縲・PS縺ッ41��い繝��縺ィ縲�7縲�9譛域悄縺ョ譁ー險倬鹸繧帝#謌�

この中からtitleとid(リンク先)を取得したい。

まず普通にparseを始める。

    func startDownload() {
        self.items = []
        if let url = URL(
            string: "http://www.apple.com/jp/pr/feeds/pr.rss"){
            //インスタンスの生成
            if let parser = XMLParser(contentsOf: url) {
                self.parser = parser
                self.parser.delegate = self
    //これにより下記メソッドが呼ばれる
                self.parser.parse()
            }
        }
    }

ここで3つのメソッドがポイント

一つは、要素の開始ごとに呼ばれるメソッド

もう一つは、CDATAごとに呼ばれるメソッド

もう一つは、要素の終了ごとに呼ばれるメソッド

     //要素の開始タグごとに呼ばれる
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String])

// CDATAを見つけるごとに呼ばれる エンコードする必要があり
    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {

   //要素の終了ごとに呼ばれるメソッド
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

エンドポイントを見ると、要素の開始タグの次にCDATAが来て、タイトルが入っている。そこで読み込みをしたい。 そのために読み込むかどうかを判断するbool型を宣言しておく。</p> <pre class="code lang-swift" data-lang="swift" data-unlink> <span class="synPreProc">var</span> <span class="synIdentifier">readOrNot</span><span class="synSpecial">:</span><span class="synType">Bool</span> <span class="synIdentifier">=</span> <span class="synConstant">false</span> <span class="synComment">//他のメンバ変数</span> <span class="synPreProc">var</span> <span class="synIdentifier">parser</span><span class="synSpecial">:</span><span class="synType">XMLParser</span><span class="synIdentifier">!</span> <span class="synPreProc">var</span> <span class="synIdentifier">items</span> <span class="synIdentifier">=</span> [Item]() <span class="synPreProc">var</span> <span class="synIdentifier">item</span><span class="synSpecial">:</span><span class="synType">Item</span>? <span class="synPreProc">var</span> <span class="synIdentifier">currentString</span><span class="synSpecial">:</span><span class="synType">String</span>? </pre> <p>これにより、CDATA部分で読み込むかどうかを判断させる方針とする。</p> <h4>これは上記要素の開始ごとに呼ばれるメソッドに記載する。</h4> <p>それによりCDATAの読み込み時に切り分けをする。 すべてのタグを読み込んだら大変なことになる。</p> <pre class="code" data-lang="" data-unlink> if elementName == "title" || elementName == "id" { readOrNot = true</pre> </div> <footer class="entry-footer"> <p class="entry-footer-section"> <span class="author vcard"><span class="fn" data-load-nickname="1" data-user-name="happy_teeth_ago">happy_teeth_ago</span></span> <span class="entry-footer-time"><a href="https://happy-teeth.hatenablog.com/entry/2018/11/07/084951"><time data-relative datetime="2018-11-06T23:49:51Z" title="2018-11-06T23:49:51Z" pubdate class="updated">2018-11-07 08:49</time></a></span> </p> <div class="hatena-star-container"> </div> <div class="hatena-star-metadata" style="display: none"> <a class="hatena-star-permalink" href="https://happy-teeth.hatenablog.com/entry/2018/11/07/084951">Swift XMLParserでCDATAをparseをする !編集途中読まないこと!</a> </div> <div class="social-buttons"> <div class="social-button-item"> <a href="https://b.hatena.ne.jp/entry/s/happy-teeth.hatenablog.com/entry/2018/11/07/084951" class="hatena-bookmark-button" data-hatena-bookmark-url="https://happy-teeth.hatenablog.com/entry/2018/11/07/084951" data-hatena-bookmark-layout="vertical-balloon" data-hatena-bookmark-lang="ja" title="この記事をはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/button-only.gif" alt="この記事をはてなブックマークに追加" width="20" height="20" style="border: none;" /></a> </div> <div class="social-button-item"> <div class="fb-share-button" data-layout="box_count" data-href="https://happy-teeth.hatenablog.com/entry/2018/11/07/084951"></div> </div> <div class="social-button-item"> <a href="https://twitter.com/share" class="twitter-share-button" data-url="https://happy-teeth.hatenablog.com/entry/2018/11/07/084951" data-count="vertical" data-text="Swift XMLParserでCDATAをparseをする !編集途中読まないこと! - happy_teeth's 忘備録" data-lang="ja">Tweet</a> </div> <span> <div class="line-it-button" style="display: none;" data-type="share-e" data-lang="ja" ></div> <script src="//scdn.line-apps.com/n/line_it/thirdparty/loader.min.js" async="async" defer="defer" ></script> </span> </div> <div class="google-afc-image test-google-rectangle-ads"> <div id="google_afc_user_container_0" class="google-afc-user-container google_afc_blocklink2_5 google_afc_boder" data-test-unit="/4374287/blog_user"></div> </div> <div class="customized-footer"> <div class="entry-footer-modules" id="entry-footer-secondary-modules"> <div class="hatena-module hatena-module-related-entries" > <!-- Hatena-Epic-has-related-entries-with-elasticsearch:true --> <div class="hatena-module-title"> 関連記事 </div> <div class="hatena-module-body"> <ul class="related-entries hatena-urllist urllist-with-thumbnails"> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://happy-teeth.hatenablog.com/entry/2018/08/22/124046"> <img alt="Rails チュートリアル following followerの関係について " src="https://cdn.image.st-hatena.com/image/square/d003741cf99976889725d1827d38fa4a126a21ff/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhappy_teeth_ago%2F20180822%2F20180822124303.png" class="urllist-image related-entries-image" title="Rails チュートリアル following followerの関係について " width="100"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://happy-teeth.hatenablog.com/archive/2018/08/22" rel="nofollow"> <time pubdate datetime="2018-08-22T03:40:46Z" title="2018-08-22T03:40:46Z"> 2018-08-22 </time> </a> </div> <a href="https://happy-teeth.hatenablog.com/entry/2018/08/22/124046" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">Rails チュートリアル following followerの関係について </a> <div class="urllist-entry-body related-entries-entry-body">この画面の左側のfollowing followerの数はどのようにして表示…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://happy-teeth.hatenablog.com/entry/2018/06/27/213150"> <img alt="swift CoreData 保存 カメラで取得した画像" src="https://cdn.image.st-hatena.com/image/square/016adba9db2ec675699d1b6c523140677e922964/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhappy_teeth_ago%2F20180628%2F20180628003710.png" class="urllist-image related-entries-image" title="swift CoreData 保存 カメラで取得した画像" width="100"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://happy-teeth.hatenablog.com/archive/2018/06/27" rel="nofollow"> <time pubdate datetime="2018-06-27T12:31:50Z" title="2018-06-27T12:31:50Z"> 2018-06-27 </time> </a> </div> <a href="https://happy-teeth.hatenablog.com/entry/2018/06/27/213150" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">swift CoreData 保存 カメラで取得した画像</a> <div class="urllist-entry-body related-entries-entry-body">まずカメラの利用には UIImagePickerController が必要。何をす…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://happy-teeth.hatenablog.com/entry/2018/06/23/023039"> <img alt="SWIFT Storyboardのsegueで遷移先に値を渡す(テーブルで選択した値)" src="https://cdn.image.st-hatena.com/image/square/83c1f77e5ffee835b68a252969b87f39a1084224/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhappy_teeth_ago%2F20180623%2F20180623022939.png" class="urllist-image related-entries-image" title="SWIFT Storyboardのsegueで遷移先に値を渡す(テーブルで選択した値)" width="100"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://happy-teeth.hatenablog.com/archive/2018/06/23" rel="nofollow"> <time pubdate datetime="2018-06-22T17:30:39Z" title="2018-06-22T17:30:39Z"> 2018-06-23 </time> </a> </div> <a href="https://happy-teeth.hatenablog.com/entry/2018/06/23/023039" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">SWIFT Storyboardのsegueで遷移先に値を渡す(テーブルで選択した値)</a> <div class="urllist-entry-body related-entries-entry-body">segueで値を引きわたす。モデルで。 まずテーブルのセルが選択…</div> </div> </li> </ul> </div> </div> </div> </div> <div class="comment-box"> <ul class="comment"> <li class="read-more-comments" style="display: none;"><a>もっと読む</a></li> </ul> <a class="leave-comment-title js-leave-comment-title">コメントを書く</a> </div> </footer> </div> </article> <!-- rakuten_ad_target_end --> <!-- google_ad_section_end --> <div class="pager pager-permalink permalink"> <span class="pager-prev"> <a href="https://happy-teeth.hatenablog.com/entry/2018/11/17/084601" rel="prev"> <span class="pager-arrow">« </span> swift git ignore Mac 特殊ファイルの除外… </a> </span> <span class="pager-next"> <a href="https://happy-teeth.hatenablog.com/entry/2018/11/01/215317" rel="next"> Swift TabBarでの値の受け渡し <span class="pager-arrow"> »</span> </a> </span> </div> </div> </div> <aside id="box1"> <div id="box1-inner"> </div> </aside> </div><!-- #wrapper --> <aside id="box2"> <div id="box2-inner"> <div class="hatena-module hatena-module-profile"> <div class="hatena-module-title"> プロフィール </div> <div class="hatena-module-body"> <a href="https://happy-teeth.hatenablog.com/about" class="profile-icon-link"> <img src="https://cdn.profile-image.st-hatena.com/users/happy_teeth_ago/profile.png" alt="id:happy_teeth_ago" class="profile-icon" /> </a> <span class="id"> <a href="https://happy-teeth.hatenablog.com/about" class="hatena-id-link"><span data-load-nickname="1" data-user-name="happy_teeth_ago">id:happy_teeth_ago</span></a> </span> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> </div> </div> <div class="hatena-module hatena-module-search-box"> <div class="hatena-module-title"> 検索 </div> <div class="hatena-module-body"> <form class="search-form" role="search" action="https://happy-teeth.hatenablog.com/search" method="get"> <input type="text" name="q" class="search-module-input" value="" placeholder="記事を検索" required> <input type="submit" value="検索" class="search-module-button" /> </form> </div> </div> <div class="hatena-module hatena-module-links"> <div class="hatena-module-title"> リンク </div> <div class="hatena-module-body"> <ul class="hatena-urllist"> <li> <a href="https://hatenablog.com/">はてなブログ</a> </li> <li> <a href="https://hatenablog.com/guide?via=200109">ブログをはじめる</a> </li> <li> <a href="http://blog.hatenablog.com">週刊はてなブログ</a> </li> <li> <a href="https://hatenablog.com/guide/pro">はてなブログPro</a> </li> </ul> </div> </div> <div class="hatena-module hatena-module-recent-entries "> <div class="hatena-module-title"> <a href="https://happy-teeth.hatenablog.com/archive"> 最新記事 </a> </div> <div class="hatena-module-body"> <ul class="recent-entries hatena-urllist "> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <a href="https://happy-teeth.hatenablog.com/entry/2020/05/03/075548" class="urllist-title-link recent-entries-title-link urllist-title recent-entries-title">Vue 忘備録 見ないこと!</a> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <a href="https://happy-teeth.hatenablog.com/entry/2020/04/21/122947" class="urllist-title-link recent-entries-title-link urllist-title recent-entries-title">Xserver WPForms からメール送付 WordPress </a> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <a href="https://happy-teeth.hatenablog.com/entry/2020/04/18/161823" class="urllist-title-link recent-entries-title-link urllist-title recent-entries-title">Docker Django3.0 環境構築</a> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <a href="https://happy-teeth.hatenablog.com/entry/2020/04/13/104415" class="urllist-title-link recent-entries-title-link urllist-title recent-entries-title">WordPress 検索フォームの実装</a> </div> </li> <li class="urllist-item recent-entries-item"> <div class="urllist-item-inner recent-entries-item-inner"> <a href="https://happy-teeth.hatenablog.com/entry/2020/04/11/100532" class="urllist-title-link recent-entries-title-link urllist-title recent-entries-title">Python スクレイピング 環境構築 jupyter notebook</a> </div> </li> </ul> </div> </div> <div class="hatena-module hatena-module-archive" data-archive-type="default" data-archive-url="https://happy-teeth.hatenablog.com/archive"> <div class="hatena-module-title"> <a href="https://happy-teeth.hatenablog.com/archive">月別アーカイブ</a> </div> <div class="hatena-module-body"> </div> </div> </div> </aside> </div> </div> </div> </div> <footer id="footer" data-brand="hatenablog"> <div id="footer-inner"> <div style="display:none !important" class="guest-footer js-guide-register test-blogs-register-guide" data-action="guide-register"> <div class="guest-footer-content"> <h3>はてなブログをはじめよう!</h3> <p>happy_teeth_agoさんは、はてなブログを使っています。あなたもはてなブログをはじめてみませんか?</p> <div class="guest-footer-btn-container"> <div class="guest-footer-btn"> <a class="btn btn-register js-inherit-ga" href="https://blog.hatena.ne.jp/register?via=200227" target="_blank">はてなブログをはじめる(無料)</a> </div> <div class="guest-footer-btn"> <a href="https://hatenablog.com/guide" target="_blank">はてなブログとは</a> </div> </div> </div> </div> <address class="footer-address"> <a href="https://happy-teeth.hatenablog.com/"> <img src="https://cdn.blog.st-hatena.com/images/admin/blog-icon-noimage.png" width="16" height="16" alt="happy_teeth's 忘備録"/> <span class="footer-address-name">happy_teeth's 忘備録</span> </a> </address> <p class="services"> Powered by <a href="https://hatenablog.com/">Hatena Blog</a> | <a href="https://blog.hatena.ne.jp/-/abuse_report?target_url=https%3A%2F%2Fhappy-teeth.hatenablog.com%2Fentry%2F2018%2F11%2F07%2F084951" class="report-abuse-link test-report-abuse-link" target="_blank">ブログを報告する</a> </p> </div> </footer> <script src="https://s.hatena.ne.jp/js/HatenaStar.js?20191001"></script> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&appId=719729204785177&version=v2.7"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> <div class="quote-box"> <div class="tooltip-quote tooltip-quote-star"> <i class="blogicon-star" title="引用スターをつける"></i> </div> <div class="tooltip-quote tooltip-quote-stock"> <i class="blogicon-quote" title="引用をストック"></i> </div> <div class="tooltip-quote tooltip-quote-tweet js-tooltip-quote-tweet"> <a class="js-tweet-quote" target="_blank" data-track-name="quote-tweet" data-track-once><i class="blogicon-twitter" title="引用してツイートする"></i></a> </div> </div> <div class="message-box" id="quote-star-message-box" style="display: none; position: absolute;"> スターをつけました </div> <div class="quote-stock-panel" id="quote-stock-message-box" style="position: absolute; z-index: 3000"> <div class="message-box" id="quote-stock-succeeded-message" style="display: none"> <p>引用をストックしました</p> <button class="btn btn-primary" id="quote-stock-show-editor-button" data-track-name="curation-quote-edit-button">ストック一覧を見る</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="message-box" id="quote-login-required-message" style="display: none"> <p>引用するにはまずログインしてください</p> <button class="btn btn-primary" id="quote-login-button">ログイン</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="quote-stock-failed-message" style="display: none"> <p>引用をストックできませんでした。再度お試しください</p> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="unstockable-quote-message-box" style="display: none; position: absolute; z-index: 3000;"> <p>限定公開記事のため引用できません。</p> </div> </div> <script type="x-underscore-template" id="js-requote-button-template"> <div class="requote-button js-requote-button"> <button class="requote-button-btn tipsy-top" title="引用する"><i class="blogicon-quote"></i></button> </div> </script> <div id="hidden-subscribe-button" style="display: none;"> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> </div> <script type="text/javascript" src="https://platform.twitter.com/widgets.js"></script> <script type="text/javascript" src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/external/jquery.min.js?version=1.12.3"></script> <script type="text/javascript" src="https://cdn7.www.st-hatena.com/js/jquery/jquery-ui.1.10.0.custom.min.js"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/external/jquery.flot.js?version=0.8.3"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/external/jquery.flot.time.js?version=0.8.3"></script> <script id="vendor-js" data-env="production" type="text/javascript" src="https://cdn.blog.st-hatena.com/js/vendor.js?version=6359bc9f80d396061c4587caa542114eaca1b6a3&env=production" crossorigin="anonymous"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/texts-ja.js?version=4d4fb9ccb036c31946e5bb8374cec39f3ba5c933&env=production"></script> <script id="hatenablog-js" data-env="production" type="text/javascript" src="https://cdn.blog.st-hatena.com/js/hatenablog.js?version=7d7b626c8f7ccee40e85a719957aec88ebb5f4cd&env=production" crossorigin="anonymous"></script> <script type="text/javascript">Hatena.Diary.GlobalHeader.init()</script> <script src="https://www.google.com/recaptcha/api.js" async defer></script> <script id="valve-dmp" data-service="blog" src="https://cdn.pool.st-hatena.com/valve/dmp.js" data-test-id="dmpjs" async></script> </body> </html>