WSDL からクライアントコードを生成しようとした際に 「Could not resolve schema import」 や 「Failed to load external schema」 といったエラーに遭遇したことはないでしょうか。WSDL ファイルは外部の XSD (XML Schema Definition) を xs:import や xs:include で参照することが多く、この参照チェーンのどこか一箇所でも解決に失敗すると、パース処理全体が停止します。
本記事では、外部スキーマインポートエラーの根本原因、xs:import と xs:include の決定的な違い、そして wsimport、wsdl2java、svcutil など各ツールでの具体的な修正方法を解説します。
xs:import と xs:include の違い
この 2 つの XSD メカニズムは見た目が似ていますが、役割が根本的に異なります。混同はエラーの典型的な原因です。
xs:include
xs:include は 同じターゲット名前空間 (または名前空間なし) のスキーマコンポーネントを現在のスキーマに統合します。型定義のコピー&ペーストに近い動作です。
<!-- 両方のスキーマが同じ targetNamespace を持つ必要がある -->
<xs:schema targetNamespace="http://example.com/types"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation="CommonTypes.xsd"/>
<xs:element name="Order" type="tns:OrderType"/>
</xs:schema>
xs:import
xs:import は 異なる名前空間 のスキーマコンポーネントを取り込みます。スキーマ間の名前空間依存関係を確立する仕組みです。
<xs:schema targetNamespace="http://example.com/orders"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:types="http://example.com/types">
<xs:import namespace="http://example.com/types"
schemaLocation="types/CommonTypes.xsd"/>
<xs:element name="Order" type="types:OrderType"/>
</xs:schema>
重要なルール: 名前空間が異なるのに xs:include を使う (またはその逆) とパースエラーが発生します。
比較表
| 特徴 | xs:include | xs:import |
|---|---|---|
| 名前空間 | 同一または未指定 | 異なる |
namespace 属性 | 使用不可 | 必須 |
schemaLocation | 必須 | 任意 |
| 意味 | 現在のスキーマに統合 | 外部名前空間を参照 |
| イメージ | C 言語の #include | Java の import |
よくあるエラーメッセージと原因
エラー 1: schemaLocation の解決失敗
org.xml.sax.SAXParseException: schema_reference.4:
Failed to read schema document 'CommonTypes.xsd',
because the document could not be read.
schemaLocation のパスが解決できない場合に発生します。最も多い原因は以下の通りです。
相対パスのベース不一致。 https://api.example.com/services/OrderService?wsdl にある WSDL が schemaLocation="../schemas/types.xsd" を参照している場合、パーサーは WSDL の URL を基準に相対パスを解決します。サーバーがその相対位置でスキーマを提供していなければ取得に失敗します。
修正方法: 絶対 URL を使用するか、相対パスの解決を検証する。
<!-- 修正前: 解決できない可能性のある相対パス -->
<xs:import schemaLocation="../schemas/types.xsd"
namespace="http://example.com/types"/>
<!-- 修正後: 確実に解決できる絶対 URL -->
<xs:import schemaLocation="https://api.example.com/schemas/types.xsd"
namespace="http://example.com/types"/>
エラー 2: ネットワーク制約によるスキーマ取得失敗
java.net.ConnectException: Connection timed out
while resolving schema at https://internal.corp.example.com/schemas/types.xsd
エンタープライズ環境の WSDL では、社内ネットワーク、ファイアウォール内、またはアクセス制限のあるサーバーに配置されたスキーマを参照していることが珍しくありません。開発マシンからスキーマの URL に到達できなければ解決は失敗します。
修正方法: スキーマをダウンロードしてローカル化する。
# 参照されているスキーマをすべてダウンロード
mkdir -p schemas
curl -o schemas/types.xsd https://internal.corp.example.com/schemas/types.xsd
curl -o schemas/common.xsd https://internal.corp.example.com/schemas/common.xsd
# WSDL をローカルファイルを参照するよう更新
wsimport の場合、カタログファイルが利用できます:
<!-- jax-ws-catalog.xml -->
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<system systemId="https://internal.corp.example.com/schemas/types.xsd"
uri="schemas/types.xsd"/>
<system systemId="https://internal.corp.example.com/schemas/common.xsd"
uri="schemas/common.xsd"/>
</catalog>
カタログを指定して実行します:
wsimport -catalog jax-ws-catalog.xml service.wsdl
エラー 3: import の namespace とスキーマの targetNamespace の不一致
s4s-att-invalid-value: Invalid attribute value for 'namespace'
in element 'import'. Recorded information indicates
the actual targetNamespace is 'http://example.com/types/v2'.
xs:import の namespace 属性が、参照先スキーマドキュメントの targetNamespace と一致しない場合に発生します。
修正方法: namespace の値を一致させる。
<!-- import の namespace はスキーマの targetNamespace と完全に一致させる -->
<xs:import namespace="http://example.com/types/v2"
schemaLocation="types-v2.xsd"/>
エラー 4: 循環参照
org.apache.xmlbeans.XmlException: error: Circular reference
detected while resolving schema imports
スキーマ A がスキーマ B をインポートし、B が C をインポートし、C が再び A をインポートする場合に発生します。XSD 仕様上は許容されますが、多くのパーサーは正しく処理できません。
修正方法: 構造を変更してサイクルを断ち切る。
修正前 (循環あり):
A.xsd → B.xsd をインポート → C.xsd をインポート → A.xsd をインポート
修正後 (循環なし):
Common.xsd (共有型をここに抽出)
A.xsd → Common.xsd をインポート
B.xsd → Common.xsd をインポート
C.xsd → Common.xsd をインポート
ツール別の対処法
wsimport (JAX-WS)
# classpath 相対パスでの解決に -wsdllocation を使用
wsimport -keep -wsdllocation /wsdl/service.wsdl \
-catalog jax-ws-catalog.xml \
-d output/ \
service.wsdl
# 自己署名証明書での HTTPS 接続
wsimport -keep \
-XdisableSSLHostnameVerification \
-Xss 4m \
https://api.example.com/service?wsdl
wsdl2java (Apache CXF)
# -catalog でスキーマ解決を指定
wsdl2java -catalog catalog.xml \
-d output/ \
-p com.example.service \
service.wsdl
# ディレクトリからインポートを解決
wsdl2java -wsdlLocation classpath:wsdl/service.wsdl \
-d output/ \
service.wsdl
svcutil (.NET)
# /reference で外部スキーマを手動指定
svcutil service.wsdl types.xsd common.xsd /out:ServiceClient.cs
.NET Core 以降では dotnet-svcutil を使用します:
dotnet tool install --global dotnet-svcutil
dotnet-svcutil service.wsdl --outputDir Generated
ローカルスキーマキャッシュ戦略
多数の外部スキーマを参照する WSDL を扱うチームでは、ローカルキャッシュ戦略によりダウンロード失敗の繰り返しを防ぎ、ビルドを高速化できます。
project/
├── wsdl/
│ ├── OrderService.wsdl
│ └── schemas/
│ ├── order-types.xsd
│ ├── common-types.xsd
│ └── external/
│ ├── xmldsig-core-schema.xsd
│ └── xenc-schema.xsd
├── catalog.xml
└── pom.xml
Maven でのローカルスキーマ解決設定:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<goals><goal>wsimport</goal></goals>
<configuration>
<wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory>
<wsdlFiles>
<wsdlFile>OrderService.wsdl</wsdlFile>
</wsdlFiles>
<catalog>${basedir}/src/main/resources/catalog.xml</catalog>
<packageName>com.example.order</packageName>
</configuration>
</execution>
</executions>
</plugin>
スキーマ解決のデバッグ
エラーメッセージからどのスキーマの読み込みに失敗したか判断できない場合は、verbose ログを有効にします:
# wsimport の verbose モード
wsimport -verbose -Xdebug service.wsdl
# Apache CXF の verbose 出力
wsdl2java -verbose -validate service.wsdl
# xmllint での簡易検証
xmllint --schema http://www.w3.org/2001/XMLSchema.xsd service.wsdl --noout
各 schemaLocation の URL が到達可能か curl で手動検証することも有効です:
# すべての schemaLocation の値を抽出してテスト
grep -oP 'schemaLocation="[^"]*"' service.wsdl | \
sed 's/schemaLocation="//;s/"//' | \
while read url; do
echo -n "Testing $url ... "
curl -s -o /dev/null -w "%{http_code}" "$url"
echo
done
SOAPless による解決
外部スキーマの解決エラーは、WSDL トラブルシューティングの中でも最も時間を消費する問題の一つです。SOAPless はこの問題をサーバーサイドで WSDL パースとスキーマ解決を処理することで根本から解消します。WSDL URL を貼り付けるだけで、xs:import と xs:include の参照はすべて自動解決されます。ファイアウォール内のスキーマ、相対パスのスキーマ、循環参照を含むスキーマにも対応しています。
結果として得られるのは、OpenAPI 3.0 ドキュメント付きのクリーンな REST JSON API です。クライアントコードの生成もカタログファイルもビルドプラグインの設定も不要です。JSON リクエストは自動的に名前空間付きの SOAP XML に変換され、レスポンスはクリーンな JSON として返されます。認証情報は AES-256-GCM 暗号化で保護され、ログには一切出力されません。
SOAPless のダッシュボードから、コードを一行も書かずに任意のオペレーションを直接テストできます。