XQueryマークアップでXMLを切り取る

Webサービスでの作業には 、SmartBear SoftwareのSoapUIが従来から使用されています。 素晴らしいツールであり、無料です。 しかし... ...これは開発者、テスター、アーキテクトのツールですが、エンドユーザーの作業を対象とするものではありません。



すでに書いたように、私は開発者ではありません。また、できれば「重砲」を使用せずに、内部および外部ソースからデータを取得する必要があります。 そのため、新しいモジュールをツールに追加し、Webサービスにアクセスし、受信したデータを解析して、消化可能な形式で表示するときが来ました。







Webサービスにアクセスするには多くの方法があります。 Pythonにはリクエストがあります (Habré1、2の記事)が、PyQt5は既に接続されているため、一部は習慣から外れ、一部は中間データ変換を削減するため、Qtツールを使用します。 したがって、受信したxml応答を変換するには、Qtに埋め込まれたXPathとXQueryを使用します。



ロシア銀行為替レートサービスに接続しますリンクサービスは、次のXMLで応答します。



<?xml version="1.0" encoding="windows-1251" ?> <ValCurs Date="16.09.2017" name="Foreign Currency Market"> <Valute ID="R01010"> <NumCode>036</NumCode> <CharCode>AUD</CharCode> <Nominal>1</Nominal> <Name> </Name> <Value>46,0614</Value> </Valute> ... </ValCurs>
      
      





パラメーターでは、DD / MM / YYYY形式の日付が送信されます。これは省略でき、コースは当日になります。



しかし、最初に、正確に何を取得したいかを決めましょう。 為替レート専用のモジュールを作成するのは面白くありません。そうしないと、新しいサービスごとに新しいモジュールが必要になることがわかります。 したがって、モジュールを汎用性で満たし、(ほぼ)すべてのサービスで動作できるようにします。すべての違いは、構成ファイルで決定する必要があります。 また、単純なサービスの場合、新しいファイルの作成が新しいモジュールの開発のように見えないように、単純なファイルが必要です。



まず、サービス、そのパラメーター、表示方法を定義するこのような構成ファイルを作成します。



 [Input] ;       Date=,02/03/2017 [WebPage] ;       Url="http://www.cbr.ru/scripts/XML_daily.asp?date_req={Date}" ; ,     Transform=valcurs.xq
      
      





urlのパラメーターの置換は、さまざまな方法で実行できます。 Qtの従来の方法はQtのQString :: arg()ですが、 QStringがないためPyQt5では使用できません。 Pythonには、%演算子とstr.format()メソッドがあります Habrについての記事もあります )。 名前付きパラメーターが必要なので、置換にも名前を付ける必要があります-str.format()はこのメソッドを提供します。 したがって、中括弧内の日付パラメーター。



結果の回答は、さまざまな方法で表示することもできます。 XMLを解析してテーブルにし(為替レートは基本的にテーブルです)、QTableWidgetに表示できます。 しかし、その後、XMLタグとテーブル列を一致させるためのルールを記述する必要があります-おそらく可能ですが、この記述は非常に複雑であるように思われます。



タグ階層を離れて、QTreeWidgetに表示できます。 ええ、しかし、その後にのみ、Winイベントビューアのような「クリアビュー」のタイプを取得します。







非常に明確です。



そこで、テンプレートエンジンを使用して回答を実行した後、すべての鳥を1石で殺し、結果をWebページとして表示し、テンプレートを含むバンドルを構成ファイルに入れることにしました。



QtでWebページを表示するために、QWebEngineViewモジュールがあります-基本的には組み込みのChromiumです。 WebKitは以前はシンプルでQtにはるかに統合されていましたが、HTML5 modの影響下で、開発者はフレームワークでのサポートを拒否しました。 私たちが持っているものと一緒に暮らすように。



最初のパートで説明したSQLウィンドウと同様に、作業ウィンドウを組み立てましょう。ただし、QTableViewの代わりにQWebEngineViewを追加します。 このウィンドウは判明します







開始しなかった場合...
...そしてあなたがWindowsを使用している場合、QtWebEngineモジュールでOpenGLを初期化できないことが理由かもしれません。 ただし、ChromiumはWebKitとは異なり、スマートであり、OpenGLが必要です。



Windowsでは、次の3つのオプションがあります

-「 デスクトップ 」-ネイティブOpenGLへの直接呼び出し

-'angle' -OpenGL呼び出しをDirectX呼び出しに変換する、GoogleのオープンソースレイヤーANGLEを使用。

-「 ソフトウェア 」-OpenGLのソフトウェア実装を通じて



作業オプションは、QGuiApplicationを作成する前に設定する必要がある環境変数QT_OPENGLを介して定義されます。



  os.environ.putenv('QT_OPENGL','software') # desktop, software, angle
      
      





変数が定義されていない場合、PyQt5(少なくとも私が持っているアセンブリ)はそれがどのように機能するかを決定しようとしています。 そして...彼は常に成功するとは限らず、アプリケーションがクラッシュします。



ソフトウェア 」は最も確実に機能します。 ここから対応するdllをダウンロードするだけ (opengl32sw-32.7zを取得しました)、解凍してQtライブラリの隣に配置するだけです(... \ Lib \ site-packages \ PyQt5 \ Qt \ bin \)。



角度 」には、d3dcompiler_4x.dllが必要です 。 それでもうまくいかない場合は、DirectXへの呼び出しをオフにすることができます。



  os.environ.putenv('QT_ANGLE_PLATFORM','warp') # d3d11, d3d9 and warp
      
      





この場合でも、メッセージがポップアップすることがあります

QtWebEngineProcess.exe -

---------------------------

, VCRUNTIME140.dll. .








このファイルはVisual Studio 2015のVisual C ++再頒布可能パッケージに含まれていますこちらからダウンロードできます。



これですべてが機能するはずです。



何をするかはわかりませんが、学ぶ必要があります。 一般的に、PyQtではクエリの作成と実行は非常に簡単です。



  #     self.man = QNetworkAccessManager(self) #  Run def run(self): try: #    values = { kp[0]:self.inputs[kp[0]].text() for kp in self.params} url = self.url.format(**values) req = QNetworkRequest(QUrl(url)) #  GET reply = self.man.get(req) reply.finished.connect(self.replyFinished)
      
      





最後の行では、応答ハンドラー関数はQtの「スロット」に関して終了信号に接続され、QObject.sender()から元のQNetworkReplyオブジェクトを取得します。



  def replyFinished(self): reply = self.sender()
      
      





この関数が応答から呼び出されるまでに、ファイルからデータを読み取ることができます。



テンプレートエンジンの選択について長い間考える必要はありませんでした-JSとPythonの両方に多くありますが( Wikiレビュー )、既にインストールされているものを使用するのは自然です-QtにはXQueryおよびXSLT変換テーブルをサポートするQXmlQueryがありますが、 制限があります。



XQueryでのテンプレートの記述は非常に簡単です。 XMLをテーブルに変換する変換レートテンプレートは次のようになります。



 xquery version "1.0" encoding "utf-8"; <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <div>   {string(/ValCurs/@Date)}</div> <table><tbody> <tr> <th> </th> <th> </th> <th></th> <th></th> <th></th> </tr> {for $i in /ValCurs/Valute return ( <tr> <td>{string($i/NumCode)}</td> <td>{string($i/CharCode)}</td> <td>{string($i/Nominal)}</td> <td>{string($i/Name)}</td> <td>{string($i/Value)}</td> </tr> ) } </tbody></table> </body> </html>
      
      





これで、テンプレートを介して回答を実行し、結果をWebページに貼り付けることができます。 便利なことに、バッファではなくQIODeviceに基づいたソースを使用して、QXmlQueryをメインドキュメントに渡すことができます。 私たちの答えはこれに非常に適しています。



  #   lang = QXmlQuery.XQuery10 query = QXmlQuery(lang) query.setMessageHandler(XmlQueryMessageHandler()) query.setFocus(reply) #     templ = QFile(self.transformTemplate) templ.open(QIODevice.ReadOnly) query.setQuery(templ) #        - out = query.evaluateToString() self.page.setHtml(out)
      
      





すべてをまとめて実行します。 動作しますか? 悲しいかな、奇跡は二度と起こらなかった、もちろんうまくいく。







XQueryの代わりに、XSLテーブルを使用できます。 1行変更するだけです



  lang = QXmlQuery.XSLT20
      
      





xmlview.xslt
 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*" /> <xsl:output method="html" version="2.0" encoding="UTF-8" indent="no"/> <xsl:template match="/"> <html> <head> <style type="text/css"> .node { font-weight: normal } .nodeName { font-weight: normal } .nodeLine { font-weight: bold } </style> </head> <body> <h2>Transformed XML</h2> <xsl:apply-templates select="/*"/> </body> </html> </xsl:template> <xsl:template match="node()"> <xsl:param name="level" select="0"/> <div> <xsl:attribute name="style"> <xsl:value-of select="concat('margin-left: ',$level,'em')"/> </xsl:attribute> <span class="nodeLine"><<span class="nodeName"><xsl:value-of select="name(.)"/></span> <xsl:for-each select="@*"><xsl:value-of select="concat(' ',name(.))"/>="<xsl:value-of select="."/>"</xsl:for-each>><xsl:value-of select="text()"/><xsl:apply-templates select="*"><xsl:with-param name="level" select="2"/></xsl:apply-templates><span class="node"></<span class="nodeName"><xsl:value-of select="name(.)"/></span>></span></span> </div> </xsl:template> </xsl:stylesheet>
      
      







このようになります







しかし、テンプレートなしで受信したXMLをページに直接渡すとどうなりますか?



  b = reply.readAll() self.page.setContent(b, reply.header(QNetworkRequest.ContentTypeHeader), reply.url())
      
      





ChromiumはこのXMLを表示できます。







Central BankサービスがHTTP応答ヘッダーにコードページを登録せず、ChromiumがXMLヘッダーを解析しなかったため、エンコーディングに違反しました。 デフォルトのコードページの強制追加を追加します。



  b = reply.readAll() cth = reply.header(QNetworkRequest.ContentTypeHeader) if len(cth.split(';')) == 1: cth = cth + ";charset=windows-1251" self.page.setContent(b,cth,reply.url())
      
      









今ではすべてが順調です。



共通のツールボックスに新しいモジュールを追加するだけです。 ソースコードはgithubにあります。



All Articles