前項の続き、メニュー定義編です。
書くこと
- intra-martのエクスポートファイル(本稿ではメニュー定義)をExcelファイルに取り込む要領
書かないこと
- PowerShellの詳細
- XSLTの詳細
- パワークエリの詳細
想定環境
- Windows
- Excel(パワークエリが利用可能なもの)
- 上記以外のツール・ライブラリをインストール・セットアップせずに済ませたい
XSLTプロセッサ
前述のとおり、セットアップを省略したいので、PowerShellでXslCompiledTransformクラスを使います。
XslCompiledTransformクラスはXSLT1.0のみに対応とのことで、早速XSLT2.0以上は使えないという制約が発生しております!![]()
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ファイルです。公式ドキュメントのサンプルファイルを拝借しております。
<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を「|」に変更する等、調整してください。)
<?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変換結果
<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で表示すると、以下のようになりました。

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

あとはExcel側でいかようにも調理できるかと思います。
このExcelファイルからxmlを生成できるようにすれば、完全循環のエコシステムが出現する、かもしれません。
なお、今回は複雑なxmlでしたのでXSLTを利用しましたが、単純なxmlであれば、そのままパワークエリで取り扱えるケースもあると思います。ご留意くださいませ。
ではまた。