xmlでやりとりしているAPIをSpringに移植してみた


更新履歴

日付 内容
2021.03.27 初版

はじめに

xmlでやりとりしている既存のAPIの置き換えの検証をしている。
API仕様書がエクセルだったりするので仕様書から作った方がいいかな、と考えている。
ひとまずこの仕組みをパクり、以下の方針で作成してみた。
・xmlでやりとりという部分は変えない
・OpenAPIから色々自動生成する
・Springで作る、もちろんJava11

OpenAPIGenerator

使用したバージョンは 5.1.0
Gradleから利用し、モードは openapispring を使用した。

mode:openapi

OpenAPIをrefを駆使して色々とファイルを分割し、mode:openapiでひとつのファイルに統合した。

mode:spring

mode:openapi で生成したopenapi.jsonからインターフェースとモデルを自動生成させた。
今回はxmlでやりとりするので、

openapi-generator-config.json
{
  "withXml": "true",
  "jackson": "false"
}

の設定を追加した。
※ "jackson": "false" は不要かも?

自動生成コードについて

生成されるモデルのクラスに @XmlRootElement(name = "{classname}") が付与されるのだが、
{classname}箇所が一律UpperCamelCaseとなっている。
※ 読んで字のごとく、クラス名となっている。
期待していた値はOpenAPIで定義しているプロパティだっただけに残念。
OpenAPIGeneratorのテンプレートを確認したところ、こうなっていた。

xmlAnnotation.mustache#L5
@XmlRootElement({{#xmlNamespace}}namespace="{{xmlNamespace}}", {{/xmlNamespace}}name = "{{#xmlName}}{{xmlName}}{{/xmlName}}{{^xmlName}}{{classname}}{{/xmlName}}")

このままだと使い物にならないので、テンプレートを自作(いうてもコピーですが)して、@XmlRootElementのみ出力し、Spring(JAXB?)の設定でSnakeCaseになるようにやってみたがうまくスネークケースにならなかった。

やりたいことが叶えられないことに六時間ぐらい割きましたが、
時間がもったいないので自動生成コードは使わないとしました。
※ モデルを使わないので、インターフェースも使わないとなりました。
※ 英断になるのでしょうか?

Spring

SpringMVCではなく、SpringWebFluxで実装してみました。
xmlを受け取ってxmlで返却するので、SpringWebFluxである必要はないです。書いてみたかっただけ。

セットアップ

build.gradleに下記の依存関係を別途、追加しました。

build.gradle
dependencies {
    // SpringBoot Projects
    implementation("org.springframework.data:spring-data-commons")
    implementation("org.springframework.boot:spring-boot-starter-hateoas")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("javax.xml.bind:jaxb-api")
    implementation("org.glassfish.jaxb:jaxb-runtime")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
}

Controller

自動生成したインターフェースのdefaultメソッドをスクラッチしたControllerにコピペして、
default -> public に置換した後中身を実装しました。
SpringFox向けのアノテーションは手動でSpringDoc向けのアノテーションに置換しました。
リクエストボディとレスポンスボディのいわゆるモデルについては、自作クラスに置換する必要があります。

Model

リクエストボディとレスポンスボディに相当する箇所については、
自動生成したモデルを参考に、コピペ作業などを実施しました。
0から作るよりは、OpenAPI Generator で自動生成したものを使い回すことで労力は削れたかと思います。
ただし、メンバ変数がLowerCamelCaseなのですが、後述するWebClientでうまくマッピングできなかったので、
メンバ変数も手作業でSnakeCaseに書き換えています。

自動生成したモデルにはJAXBのアノテーションが付与されていました。

WebClient

API呼び出しをWebClientを使って実装してみました。
リクエストで受け取ったxmlをそのまま外部APIにたらい回すような作りにしました。
問題があって、
SnakeCaseで定義されたプロパティはJavaのメンバ変数でもSnakeCaseとなっていないとまともに動作しませんでした。
SpringとJAXBの仕様と思いますが、僕には解決できませんでした。

おわりに

既存API(xml)をOpenAPI化と自動生成&Springの仕組みに乗っけるのは辛かったです。
そもそも既存が RestWebAPI もしくは OpenAPI Specification 3.x.x の仕様からかけ離れすぎている

とはいえ、xmlでやりとりする ってことも Spring でできることを確認できたのはお勉強になりました。