セレスアドベントカレンダー24日目です。
セレスでWebエンジニアをやっている@ko-sasakiです。不動産や注文住宅系のメディアのサービス開発を行っています。当メディアはよくあるWordpressではなく、独自で記事管理システムを作っています。記述はHTMLでもマークダウンでもいいのですが、マークダウンを推奨しています。これは記事の可読性や編集等をしやすく狙いがあります。マークダウンで入力されたものをHTML等に出力するために、システム内でflexmark-javaを結構使っているので、その紹介をさせていただきます。
Javaのマークダウンパーサたち
Javaもいくつかマークダウンパーサの実装があって検討しました。
-
commonmark-java(https://github.com/atlassian/commonmark-java)
アトラシアンがメンテしているマークダウンパーサです。CommonMarkというマークダウン仕様を実装しているものになります。CommonMarkは他にもPerlやRubyなどの複数の言語で実装されています。拡張ポイントがあるので、本体をさわることなく機能拡張はできそう。 -
markedj(https://github.com/gitbucket/markedj)
gitbucket製のマークダウンパーサ。あんまり活発ではなさそうで、拡張機能とかもなさそうでした。 -
flexmark-java(https://github.com/vsch/flexmark-java)
今回使用したマークダウンパーサで、pagdown,kramdown,commonMarkなどにも対応しているもようです。開発も結構活発で、最近Java9のmodule対応とかもしていました。またPDFやDOCXなどにも出力とかできるので、簡単な帳票出力のテンプレートとしても使えそうでした。拡張機能もあり、本体のソースコードをさわることなく、拡張が可能です。また性能もそこそこよく、10万文字くらいまでは普通に変換されます。(一部パーサでは2万文字くらいで落ちたりしていました)
調べたときは色々あったんですけど、結構Deprecated
になっていたので、割愛しております。
設定と出力
マークダウンパーサはとても簡単でオプション設定して、マークダウンをパーサにかけます。
出力後はcom.vladsch.flexmark.ast.Document型になります。HTMLやPDFの変換はその後に行います。
べた書きするとこんな感じのコードになります。
String source = "## title"
MutableDataSet option = new MutableDataSet();
option.setFrom(ParserEmulationProfile.MULTI_MARKDOWN)
.set(Parser.EXTENSIONS, Arrays.asList(
AbbreviationExtension.create() // エイリアスまわりの拡張
, DefinitionExtension.create() // 定義まわりの拡張
, TablesExtension.create() // テーブルまわりの拡張
, TocExtension.create() // 目次まわりの拡張
, TypographicExtension.create()
, AttributesExtension.create() // タグ内の属性追加の拡張
));
Parser parser = Parser.builder(option).build(); // パーサーにオプションを付与する
Document doc = parser.parse(source); // markdownをパースしてDocument型に変換する
HtmlRenderer renderer = HtmlRenderer.builder(option).build(); // HTMLに変換するオプションを指定する
String html = renderer.render(doc) // Document型のデータをHTMLに変換する
サンプル
H2〜H4タグや、リストタグ、テーブルタグ、aタグなど普通のマークダウンと同じ書き方でちゃんと出力できます。
変換前
## H2
リスト
- 1
- 2
- 3
### H3
リスト
+ 1
+ 2
+ 3
#### H4
オーダーリスト
1.
1.
1.
##### リンク
[google](https://google.com)
##### テーブル
|head1|head2|
|----|----|
|contents1|contents2|
変換後
<h2 id="h2">H2</h2>
<p>リスト</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<h3 id="h3">H3</h3>
<p>リスト</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<h4 id="h4">H4</h4>
<p>オーダーリスト</p>
<ol>
<li></li>
<li></li>
<li></li>
</ol>
<h5 id="リンク">リンク</h5>
<p><a href="https://google.com">google</a></p>
<h5 id="テーブル">テーブル</h5>
<table>
<thead>
<tr>
<th>head1</th>
<th>head2</th>
</tr>
</thead>
<tbody>
<tr>
<td>contents1</td>
<td>contents2</td>
</tr>
</tbody>
</table>
と簡単な紹介だとこんな感じになります。
markdownに属性を付与する
マークダウンは一般的にはclassとかstyleとか付与できません。とはいえ、Webデザインではclass指定とかここだけstyleしたいとかそういうのは、日常茶飯事かと思います。flexmark-javaだと、AttributesExtensionがあり、属性情報を追加で付与することができます。
記法
普通にマークダウンを書き、末尾に{属性=値}
を記述します。
[通常]
[google](https://google.com)
↓
<p><a href="https://google.com">google</a></p>
[属性付与]
[google](https://google.com){target="_blank"}
↓
<p><a href="https://google.com" target="_blank">google</a></p>
上記のとおり、通常だと、普通のaタグですが、{}
で属性を付与することができます。これは基本どこでも使用でき、不動産投資Oh!Ya、持ち家計画のサイト内でもいたるところに使われています。また、テーブル等で一部のセルだけ強調表示したいときにも使います。
持ち家計画の例です。
下記のコードを書くと、そのセルにだけclassがあたります。これでライターの要望にも応えています。なお、行に対しては当てられません。
|都道府県名|建築費+土地代|
|---|---|
|北海道|3,226万円|
|青森県|3,156万円|
|{class="ptc-Table_Highlight_Blue"}岩手県|{class="ptc-Table_Highlight_Blue"}3,154万円|
目次(TOC,table of contens)を自動生成する
サーバ側で目次の自動生成まで行います。Javascriptで実装してもよかったんですが、生成までに時間がかかるのと、記事によって目次をつけたい、つけたくないみたい要望に応えるためにサーバ側で実装することにしました。flexmark-javaのTocExtension
を使用します。
option.set(TocExtension.DIV_CLASS, "pst-Table_Contents"); // class指定
option.set(TocExtension.TITLE_LEVEL, 32); // 見出しの大きさ
option.set(TocExtension.TITLE, "目次"); // 見出しの文言
option.set(TocExtension.LEVELS, 4 | 8 | 16 ); // H2〜H4まで目次生成する
これをセットします。
これでマークダウンのテキストに[TOC]
と書けば、その場所に目次が生成されます。
どこでもいいので、[TOC]
と書くだけで、H2〜H4
のタグの目次が自動生成されます
[TOC]
## H2-1
### H2-1-H3
## H2-2
### H2-2-H3
変換後は下記になります。
<div class="pst-Table_Contents">
<h6>目次</h6>
<ul>
<li>
<a href="#h21">H2-1</a>
<ul>
<li>
<a href="#h21h3">H2-1-H3</a>
<ul>
<li><a href="#h21h3">H2-1-H3</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#h22">H2-2</a>
<ul>
<li>
<a href="#h22h3">H2-2-H3</a>
<ul>
<li><a href="#h22h3">H2-2-H3</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
これで目次が生成されます。不動産投資Oh!Yaではスタイルをあてて、このように表示されます。
最後に
ライターにマークダウンで記事を書いてもらいつつも、目次を自動生成したりスタイルあてたりをflexmark-javaで実現しています。また必要になれば、拡張用のAPIがあるので、それを元に独自拡張を作る予定でいます。では、良い年末を!