If you're maintaining a SOAP service and need to document it for a team that thinks in REST and OpenAPI, converting your WSDL to an OpenAPI (Swagger) specification is a practical step forward. It bridges the gap between legacy SOAP infrastructure and modern API tooling — enabling Swagger UI documentation, client code generation, and integration with API gateways that speak OpenAPI natively.
This guide covers both manual and automated approaches, explains the mapping between SOAP/WSDL concepts and OpenAPI constructs, and highlights the edge cases that trip people up.
Why Convert WSDL to OpenAPI?
- Modern documentation. Swagger UI and Redoc render OpenAPI specs into interactive API docs. There's no equivalent experience for WSDL files.
- Client generation. Tools like
openapi-generatorproduce clients in 50+ languages from an OpenAPI spec. WSDL-based code generation is limited to a few languages with aging tooling. - API gateway compatibility. Kong, AWS API Gateway, and Apigee all import OpenAPI specs natively. WSDL support is limited or nonexistent.
- Developer onboarding. New developers are far more likely to understand REST/JSON than SOAP/XML.
The Conceptual Mapping
Before converting, understand how SOAP concepts map to REST:
| WSDL Concept | OpenAPI Equivalent |
|---|---|
<wsdl:service> | Server object |
<wsdl:portType> / <wsdl:operation> | Path + HTTP method |
<wsdl:message> + <wsdl:part> | Request/response body schema |
<xs:complexType> | JSON Schema object in components/schemas |
<xs:simpleType> | JSON Schema primitive with constraints |
<soap:address location="..."> | servers[0].url |
| SOAPAction header | Not needed (path-based routing) |
| SOAP fault | Error response schema (4xx/5xx) |
Approach 1: Manual Conversion
For services with a small number of operations, manual conversion gives you the most control and produces the cleanest output.
Step 1: Extract Operations
Open the WSDL and identify all operations in the <wsdl:portType>:
<wsdl:portType name="UserServicePort">
<wsdl:operation name="GetUser">
<wsdl:input message="tns:GetUserRequest"/>
<wsdl:output message="tns:GetUserResponse"/>
<wsdl:fault name="UserNotFound" message="tns:UserNotFoundFault"/>
</wsdl:operation>
<wsdl:operation name="CreateUser">
<wsdl:input message="tns:CreateUserRequest"/>
<wsdl:output message="tns:CreateUserResponse"/>
</wsdl:operation>
</wsdl:portType>
Step 2: Map Operations to Paths
Each SOAP operation becomes an OpenAPI path. Choose RESTful paths and HTTP methods:
openapi: "3.0.3"
info:
title: User Service
version: "1.0.0"
servers:
- url: https://api.example.com/services
paths:
/users/{userId}:
get:
operationId: getUser
summary: Retrieve a user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: integer
responses:
"200":
description: User found
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"404":
description: User not found
/users:
post:
operationId: createUser
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateUserRequest"
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
Step 3: Convert XSD Types to JSON Schema
This is where most of the work lives. Map each xs:complexType from the WSDL's <wsdl:types> section to a JSON Schema definition:
<!-- XSD -->
<xs:complexType name="User">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string" minOccurs="0"/>
<xs:element name="role" type="tns:RoleEnum"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="RoleEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="admin"/>
<xs:enumeration value="user"/>
<xs:enumeration value="viewer"/>
</xs:restriction>
</xs:simpleType>
# JSON Schema (OpenAPI)
components:
schemas:
User:
type: object
required: [id, name]
properties:
id:
type: integer
name:
type: string
email:
type: string
role:
$ref: "#/components/schemas/RoleEnum"
RoleEnum:
type: string
enum: [admin, user, viewer]
Type mapping reference:
| XSD Type | JSON Schema Type |
|---|---|
xs:string | string |
xs:int, xs:integer | integer |
xs:long | integer (format: int64) |
xs:float, xs:double | number |
xs:boolean | boolean |
xs:dateTime | string (format: date-time) |
xs:date | string (format: date) |
xs:base64Binary | string (format: byte) |
xs:decimal | string (pattern-constrained) |
Approach 2: Automated Tools
Several tools automate the WSDL-to-OpenAPI conversion:
APImatic Transformer
APImatic offers a cloud-based conversion service that handles WSDL-to-OpenAPI conversion. Upload your WSDL file, select OpenAPI 3.0 as the target format, and download the result. It handles complex XSD inheritance, but the output often needs manual cleanup.
wsdl-to-openapi (npm)
npm install -g wsdl-to-openapi
wsdl-to-openapi service.wsdl -o openapi.yaml
This CLI tool does a reasonable job with straightforward WSDLs but struggles with:
- Circular type references
xs:anyandxs:anyAttributewildcards- WS-Security policy definitions
- RPC/encoded style bindings
SoapUI + Export
SoapUI Pro can import a WSDL and export an OpenAPI spec through its API definition export feature. This is useful if you're already using SoapUI for testing.
Edge Cases That Break Automated Conversion
- Inheritance (
xs:extension). XSD supports complex type inheritance. JSON Schema usesallOffor this, but the mapping isn't always 1:1. - Nillable elements. XSD's
nillable="true"has no direct JSON Schema equivalent. You need to decide whether to make the field nullable or optional. - SOAP headers. Many SOAP services pass data in SOAP headers (authentication tokens, transaction IDs). These become HTTP headers or request body fields in OpenAPI, depending on your design.
- Overloaded operations. SOAP allows multiple operations with the same name but different message signatures. REST doesn't — you'll need to create distinct paths or use request body variations.
A Different Approach: Skip the Spec, Get Working Endpoints
If your goal isn't to document the SOAP service but to actually consume it as a REST API, converting the WSDL to an OpenAPI spec is only the first step. You still need to build the translation layer that maps REST requests to SOAP envelopes and back.
SOAPless takes a different approach. Instead of converting the specification, it converts the service itself. Point it at your WSDL URL, and it generates working REST endpoints that accept JSON and return JSON — complete with an auto-generated OpenAPI spec that you can use with Swagger UI, client generators, or API gateways. The SOAP-to-REST translation happens at runtime, not at the documentation level.
Whether you convert the spec manually, use automated tools, or let a proxy handle it, the mapping concepts in this guide apply. Understanding how WSDL constructs correspond to OpenAPI constructs will help you evaluate the quality of any automated conversion output.