wsdlxsdtroubleshootingweb-services

WSDL 外部スキーマインポートエラー: XSD の include と import の解決方法

SOAPless Team9 min read

WSDL からクライアントコードを生成しようとした際に 「Could not resolve schema import」「Failed to load external schema」 といったエラーに遭遇したことはないでしょうか。WSDL ファイルは外部の XSD (XML Schema Definition) を xs:importxs:include で参照することが多く、この参照チェーンのどこか一箇所でも解決に失敗すると、パース処理全体が停止します。

本記事では、外部スキーマインポートエラーの根本原因、xs:importxs:include の決定的な違い、そして wsimportwsdl2javasvcutil など各ツールでの具体的な修正方法を解説します。

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:includexs:import
名前空間同一または未指定異なる
namespace 属性使用不可必須
schemaLocation必須任意
意味現在のスキーマに統合外部名前空間を参照
イメージC 言語の #includeJava の 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:importnamespace 属性が、参照先スキーマドキュメントの 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:importxs:include の参照はすべて自動解決されます。ファイアウォール内のスキーマ、相対パスのスキーマ、循環参照を含むスキーマにも対応しています。

結果として得られるのは、OpenAPI 3.0 ドキュメント付きのクリーンな REST JSON API です。クライアントコードの生成もカタログファイルもビルドプラグインの設定も不要です。JSON リクエストは自動的に名前空間付きの SOAP XML に変換され、レスポンスはクリーンな JSON として返されます。認証情報は AES-256-GCM 暗号化で保護され、ログには一切出力されません。

SOAPless のダッシュボードから、コードを一行も書かずに任意のオペレーションを直接テストできます。