0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

intra-martのxmlをXSLT1.0でなんとかしてExcel管理したい話[メニュー定義編]

Posted at

前項の続き、メニュー定義編です。

書くこと

  • intra-martのエクスポートファイル(本稿ではメニュー定義)をExcelファイルに取り込む要領

書かないこと

  • PowerShellの詳細
  • XSLTの詳細
  • パワークエリの詳細

想定環境

  • Windows
  • Excel(パワークエリが利用可能なもの)
  • 上記以外のツール・ライブラリをインストール・セットアップせずに済ませたい

XSLTプロセッサ

前述のとおり、セットアップを省略したいので、PowerShellでXslCompiledTransformクラスを使います。
XslCompiledTransformクラスはXSLT1.0のみに対応とのことで、早速XSLT2.0以上は使えないという制約が発生しております!:laughing:

transform.ps1
Param(
    [Parameter(Mandatory=$true,HelpMessage="Enter xsl styleSheet file name.")]
    [String]$Xsl,
    [Parameter(Mandatory=$true,HelpMessage="Enter xml file name.")]
    [String]$Xml,
    [Parameter(Mandatory=$true,HelpMessage="Enter output file name.")]
    [String]$Result
)

Push-Location $PSScriptRoot
$oldDotNetCwd = [System.IO.Directory]::GetCurrentDirectory()
try {
    [System.IO.Directory]::SetCurrentDirectory($PSScriptRoot)

    # PowerShellパス → 実ファイルパスに変換
    $xslPath    = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Xsl)
    $xmlPath    = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Xml)
    $resultPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Result)

    $xslt = [System.Xml.Xsl.XslCompiledTransform]::new($true)
    $xslt.Load($xslPath)
    $xslt.Transform($xmlPath, $resultPath)
}
finally {
    [System.IO.Directory]::SetCurrentDirectory($oldDotNetCwd)
    Pop-Location
}

変換処理

XSLファイル、XMLファイル、XSLT変換後のファイルを指定して実行します。
各ファイルの内容は後述します。

.\transform.ps1 -Xsl .\menu.xsl -Xml .\menu.xml -Result .\menu.html

以下のエラーが出る場合はExecutionPolicyを指定して再度、実行します。

このシステムではスクリプトの実行が無効になっているため、ファイル {ファイルパス} を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参
照してください。

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force

XMLファイル

intra-martのメニュー定義のXMLファイルです。公式ドキュメントのサンプルファイルを拝借しております。

menu.xml
<root xmlns="http://intra-mart.co.jp/im_menu/menu-group-data">
    <menu-group-data id="menu-group-1">
        <category id="im_sitemap_pc"></category>
        <menu-item menu-id="menu-group-1" sort-number="1" type="folder" method="get" use-iframe="false" use-popup="false">
            <display-names>
                <display-name locale="ja">メニューグループ1</display-name>
                <display-name locale="en">menu group 1</display-name>
            </display-names>
            <menu-item menu-id="menu-item-1-1" sort-number="0" type="item" url="xxxx/xxx/xxx" method="get" use-iframe="false" use-popup="false">
                <display-names>
                    <display-name locale="ja">メニューアイテム1-1</display-name>
                    <display-name locale="en">menu item 1-1</display-name>
                </display-names>
            </menu-item>
            <menu-item menu-id="menu-item-1-2" sort-number="1" type="item" url="xxxx/xxx/xxx" method="get" use-iframe="false" use-popup="false">
                <display-names>
                    <display-name locale="ja">メニューアイテム1-2</display-name>
                    <display-name locale="en">menu 1-2</display-name>
                </display-names>
            </menu-item>
            <menu-item menu-id="menu-folder-1-3" sort-number="2" type="folder" url="empty" method="get" use-iframe="false" use-popup="false">
                <display-names>
                    <display-name locale="ja">メニューフォルダ1-3</display-name>
                    <display-name locale="en">menu folder 1-3</display-name>
                </display-names>
                <menu-item menu-id="menu-item-1-3-1" sort-number="1" type="item" url="xxxx/xxx/xxx" image-path="" method="get" use-iframe="true" use-popup="false" icon-16="" icon-32="" icon-48="">
                    <display-names>
                        <display-name locale="ja">メニューアイテム1-3-1</display-name>
                        <display-name locale="en">menu item 1-3-1</display-name>
                    </display-names>
                </menu-item>
            </menu-item>
        </menu-item>
    </menu-group-data>
    <menu-group-data id="menu-group-2">
        <category id="im_sitemap_pc"></category>
        <menu-item menu-id="menu-group-2" sort-number="2" type="folder" url="empty" method="get" use-iframe="false" use-popup="false">
            <display-names>
                <display-name locale="ja">メニューグループ2</display-name>
                <display-name locale="en">menu group 2</display-name>
            </display-names>
            <menu-item menu-id="menu-item-2-1" sort-number="0" type="item" url="xxxx/xxx/xxx" image-path="" method="get" use-iframe="false" use-popup="false" icon-16="" icon-32="" icon-48="">
                <display-names>
                    <display-name locale="ja">メニューアイテム2-1</display-name>
                    <display-name locale="en">menu item 2-1</display-name>
                </display-names>
            </menu-item>
        </menu-item>
    </menu-group-data>
</root>

XSLファイル

メニューの階層の深さはまちまちですので、ここでは割り切って階層を「/」区切りで連結し単一の文字列にしています。後でExcelファイルにするので、パワークエリで分割します。(メニューID等に「/」を利用している場合はxslファイル内の区切り文字separatorを「|」に変更する等、調整してください。)

menu.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:im="http://intra-mart.co.jp/im_menu/menu-group-data">

    <xsl:output method="html" indent="yes" encoding="UTF-8" />

    <xsl:template match="/">
        <html lang="ja">
            <head>
                <title>menu</title>
            </head>
            <body>
                <h2>menu</h2>
                <table border="1">
                    <thead>
                        <tr>
                            <th>category-id</th>
                            <th>full-path</th>
                            <th>type</th>
                            <th>sort-number</th>
                            <th>url</th>
                            <th>use-iframe</th>
                            <th>use-popup</th>
                            <th>display-name-ja</th>
                            <th>display-name-en</th>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:for-each select="im:root/im:menu-group-data">
                            <xsl:variable name="categoryId" select="im:category/@id" />
                            <xsl:apply-templates select="im:menu-item">
                                <xsl:with-param name="categoryId" select="$categoryId" />
                                <xsl:with-param name="parentPath" select="''" />
                            </xsl:apply-templates>
                        </xsl:for-each>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="im:menu-item">
        <xsl:param name="categoryId" />
        <xsl:param name="parentPath" />
        <xsl:variable name="fullPath">
            <xsl:choose>
                <xsl:when test="$parentPath = ''">
                    <xsl:value-of select="@menu-id" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="concat($parentPath, '/', @menu-id)" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <tr>
            <td><xsl:value-of select="$categoryId" /></td>
            <td><xsl:value-of select="$fullPath" /></td>
            <td><xsl:value-of select="@type" /></td>
            <td><xsl:value-of select="@sort-number" /></td>
            <td><xsl:value-of select="@url" /></td>
            <td><xsl:value-of select="@use-iframe" /></td>
            <td><xsl:value-of select="@use-popup" /></td>
            <td><xsl:value-of select="im:display-names/im:display-name[@locale='ja']" /></td>
            <td><xsl:value-of select="im:display-names/im:display-name[@locale='en']" /></td>
        </tr>

        <xsl:apply-templates select="im:menu-item">
            <xsl:with-param name="categoryId" select="$categoryId" />
            <xsl:with-param name="parentPath" select="$fullPath" />
        </xsl:apply-templates>
    </xsl:template>

</xsl:stylesheet>

XSLT変換結果

menu.html
<html lang="ja" xmlns:im="http://intra-mart.co.jp/im_menu/menu-group-data">
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>menu</title>
  </head>
  <body>
    <h2>menu</h2>
    <table border="1">
      <thead>
        <tr>
          <th>category-id</th>
          <th>full-path</th>
          <th>type</th>
          <th>sort-number</th>
          <th>url</th>
          <th>use-iframe</th>
          <th>use-popup</th>
          <th>display-name-ja</th>
          <th>display-name-en</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-1</td>
          <td>folder</td>
          <td>1</td>
          <td>
          </td>
          <td>false</td>
          <td>false</td>
          <td>メニューグループ1</td>
          <td>menu group 1</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-1/menu-item-1-1</td>
          <td>item</td>
          <td>0</td>
          <td>xxxx/xxx/xxx</td>
          <td>false</td>
          <td>false</td>
          <td>メニューアイテム1-1</td>
          <td>menu item 1-1</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-1/menu-item-1-2</td>
          <td>item</td>
          <td>1</td>
          <td>xxxx/xxx/xxx</td>
          <td>false</td>
          <td>false</td>
          <td>メニューアイテム1-2</td>
          <td>menu 1-2</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-1/menu-folder-1-3</td>
          <td>folder</td>
          <td>2</td>
          <td>empty</td>
          <td>false</td>
          <td>false</td>
          <td>メニューフォルダ1-3</td>
          <td>menu folder 1-3</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-1/menu-folder-1-3/menu-item-1-3-1</td>
          <td>item</td>
          <td>1</td>
          <td>xxxx/xxx/xxx</td>
          <td>true</td>
          <td>false</td>
          <td>メニューアイテム1-3-1</td>
          <td>menu item 1-3-1</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-2</td>
          <td>folder</td>
          <td>2</td>
          <td>empty</td>
          <td>false</td>
          <td>false</td>
          <td>メニューグループ2</td>
          <td>menu group 2</td>
        </tr>
        <tr>
          <td>im_sitemap_pc</td>
          <td>menu-group-2/menu-item-2-1</td>
          <td>item</td>
          <td>0</td>
          <td>xxxx/xxx/xxx</td>
          <td>false</td>
          <td>false</td>
          <td>メニューアイテム2-1</td>
          <td>menu item 2-1</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

Google Chromeで表示すると、以下のようになりました。
image1.png

Excelファイルへの取込み

Excelで「データ > データの取得と変換 > Webから」先のhtmlファイルを取込み、区切り文字で分割する等調整するとメニューの表は以下のようになります。
image2.png

あとはExcel側でいかようにも調理できるかと思います。
このExcelファイルからxmlを生成できるようにすれば、完全循環のエコシステムが出現する、かもしれません。

なお、今回は複雑なxmlでしたのでXSLTを利用しましたが、単純なxmlであれば、そのままパワークエリで取り扱えるケースもあると思います。ご留意くださいませ。

ではまた。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?