Skip to content
PYD ENTERPRISES

Migration guide

WCF to REST: DataContracts to JSON Schema, FaultContracts to error responses

WCF contracts are already formal, which makes them a good starting point and a trap: the serializer adds behavior the attributes do not show. This guide covers both and the workflow to a reviewed OpenAPI document.

WCFREST / OpenAPI 3.1
DIFFICULTY
Moderate
OUTPUT
Rule / schema JSON
SYNTAX TRAPS
6 covered
WORKFLOW
6 steps

By Pranav Kulkarni, Head of Language Engineering · Updated 15 Oct 2026

Technical syntax challenges

Where WCF migrations go wrong.

01

IsRequired does not mean non-null

[DataMember(IsRequired = true)] requires the element to be present, but a reference type can still be sent as nil. In JSON Schema that is a required property whose type includes null, unless the service code rejects nulls, which the extractor checks.

02

Missing value types become defaults

A DateTime or decimal DataMember that is not required and not sent deserializes to DateTime.MinValue or 0. Business logic downstream may treat 0001-01-01 as 'no date'. That convention must become an explicit optional field.

03

Name, Order and EmitDefaultValue

Name = "LossDt" changes the wire name, Order fixes element order in XML, and EmitDefaultValue = false omits default values from responses. Clients written against the wire format depend on all three, so the schema uses wire names and documents omission.

04

KnownType polymorphism

[KnownType(typeof(AutoClaim))] lets a base-typed member carry subclasses, signaled by xsi:type in XML or __type in WCF JSON. In OpenAPI this becomes oneOf with a discriminator property, which needs a name the business agrees on.

05

Faults and one-way operations

[FaultContract(typeof(PolicyNotFoundFault))] defines typed errors that map to HTTP statuses with problem details bodies. IsOneWay = true operations return nothing and map to 202 Accepted with an asynchronous follow-up.

06

Binding semantics

wsHttpBinding with message security, reliable sessions or transactions flowing over WS-AtomicTransaction carry guarantees that plain HTTP does not. The spec lists which operations relied on them so the REST design can provide equivalents.

Extraction workflow

6 steps from source to signed-off spec.

The sequence a PYD ENTERPRISES Discovery Scan runs for this stack. Every output links back to source file and line range.

  1. 1

    Ingest service code, config and WSDL

    Provide service and contract assemblies or source, web.config or app.config with bindings and behaviors, and any published WSDL and XSD for cross-checking.

  2. 2

    Resolve contracts and serializer settings

    PYD ENTERPRISES reads ServiceContract, OperationContract, DataContract, DataMember, EnumMember, KnownType and FaultContract attributes and the serializer each endpoint uses.

  3. 3

    Analyze service implementations

    Service methods are parsed to find null checks, default-value conventions and validation that tighten or loosen what the contract attributes say.

  4. 4

    Map bindings to transport requirements

    Security mode, sessions, transaction flow and one-way operations are recorded per endpoint as non-functional requirements.

  5. 5

    Synthesize OpenAPI 3.1 and JSON Schema

    Operations, schemas with wire names, oneOf polymorphism and fault-derived error responses are exported, each with source links.

  6. 6

    Generate contract tests

    Fixtures with the old wire format and the new JSON are generated so both endpoints can be tested side by side during migration.

Legacy in, specification out

The source, and what PYD ENTERPRISES extracts from it.

Left: the WCF construct as it sits in production. Right: the reviewed artifact your REST / OpenAPI 3.1 engineers build against. PYD ENTERPRISES produces the spec and the tests; your team writes the service.

LEGACY SOURCE

IClaimService.csC#
1[ServiceContract]
2public interface IClaimService
3{
4 [OperationContract]
5 [FaultContract(typeof(PolicyNotFoundFault))]
6 ClaimResult SubmitClaim(ClaimRequest request);
7}
8
9[DataContract(Namespace = "http://schemas.example.org/claims/2014")]
10[KnownType(typeof(AutoClaim))]
11[KnownType(typeof(PropertyClaim))]
12public class ClaimRequest
13{
14 [DataMember(IsRequired = true, Order = 1)]
15 public string PolicyNumber { get; set; }
16
17 [DataMember(IsRequired = true, Order = 2)]
18 public decimal LossAmount { get; set; }
19
20 [DataMember(Name = "LossDt", Order = 3)]
21 public DateTime LossDate { get; set; }
22
23 [DataMember(EmitDefaultValue = false)]
24 public string AdjusterCode { get; set; }
25}

EXTRACTED ARTIFACT

ClaimRequest.schema.jsonJSON
1{
2 "$schema": "https://json-schema.org/draft/2020-12/schema",
3 "$id": "urn:claims:2014:ClaimRequest",
4 "title": "ClaimRequest",
5 "type": "object",
6 "required": ["claimType", "PolicyNumber", "LossAmount"],
7 "properties": {
8 "claimType": { "enum": ["AutoClaim", "PropertyClaim"] },
9 "PolicyNumber": { "type": ["string", "null"] },
10 "LossAmount": { "type": "number" },
11 "LossDt": {
12 "type": "string",
13 "format": "date-time",
14 "$comment": "absent = DateTime.MinValue in WCF; 3 callers test for it"
15 },
16 "AdjusterCode": { "type": "string" }
17 },
18 "x-pyd-source": "IClaimService.cs:9-27",
19 "x-pyd-faults": { "PolicyNotFoundFault": 404 }
20}

Architectural best practices

Build the REST / OpenAPI 3.1 side to the spec, not to the old shape.

  • Use the wire names from DataMember Name in the new contract unless you are versioning clients at the same time.
  • Turn MinValue and zero conventions into explicit optional fields and fix the downstream checks that relied on them.
  • Map each FaultContract to a documented status and an RFC 9457 problem details body.
  • Give polymorphic types an explicit discriminator property and keep the KnownType list as its enum.
  • Run old and new endpoints side by side with the generated contract tests until clients have moved.

Why formal contracts still need extraction

It is tempting to export the WSDL, run a converter and call the result an OpenAPI document. That gives you structure, not behavior. The WSDL does not tell you that PolicyNumber can be null on the wire, that three callers treat a missing date as 'unknown' through DateTime.MinValue, or that the service implementation rejects loss amounts above a threshold. Those come from the code.

PYD ENTERPRISES reads contracts, configuration and implementation together and exports an OpenAPI 3.1 document and JSON Schemas that reflect what the service really accepts and returns, with source links. Your team builds the REST service; PYD ENTERPRISES does not generate it.

Versioning considerations

WCF services often implement IExtensibleDataObject so unknown members round-trip without loss, which let old and new clients coexist. A REST replacement needs an equivalent policy: additionalProperties behavior in the schema, a versioning scheme in the path or media type, and a rule for how long old fields are accepted. The extracted spec records which DataContracts were extensible and which fields were added later (from Order gaps and namespace changes), which gives architects the evidence to choose that policy.

FAQ

WCF to REST / OpenAPI 3.1 questions

Does PYD ENTERPRISES support WCF services that use XmlSerializer instead of DataContractSerializer?

Yes. Services marked with XmlSerializerFormat are analyzed with XmlSerializer rules, including XmlElement and XmlAttribute attributes, which change names and optionality differently.

Can it read only the WSDL if source is unavailable?

A WSDL and XSD import gives contract structure. Without service code, the behavior-dependent parts such as null handling and default conventions are flagged as unverified in the review queue.

What about duplex and streaming contracts?

Callback contracts and streamed operations are recorded and flagged; they usually map to AsyncAPI 2.6 event channels or chunked endpoints, which architects decide during review.

Get the spec for your WCF estate.

Request a codebase scan and review extracted rules, contracts and fixtures for your own WCF estate, inside your VPC.