Sunday, August 1, 2010

Converting WCF WSDL to single file (FlatWSDL) - correctly!

Note! This post is slightly different than most FlatWSDL posts, and fixes the bug with removing the <XS-import>’s.

 

The team behind WCF chose to split all generated WSDL-definitions into multiple files, one for each namespace, schema, etc. That’s a valid approach, and most modern tools support that out of the box, it’s done correctly after the WSDL standard.

 

However, not all tools support it. Various blog posts discuss different tools, and in my case it was a mainframe integration that caused the problem. I needed to get WCF to output it as one file. The good news is that’s really simple. You can use either FlatWSDL by Thinktecture or FlatWSDL in WCFExtras (They’re almost identical).

 

The only problem is that both solutions emit a WSDL file without appropriate <XS:Import>’s for the schemas. Many tools are able to resolve the references without the import, but not all are as forgiving. This post describes a code change to WCFExtras that will fix it, but I’ll add mine here as well.

 

What you need to do is to download one of the FlatWSDL approaches (This post describes it, as well as a host of others), then change the code in ExportEndpoint in FlatWsdl.cs to what I’ve included below, and you’re good to go.

 

public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
{
XmlSchemaSet generatedXmlSchemas = exporter.GeneratedXmlSchemas;

foreach (ServiceDescription generatedWsdl in exporter.GeneratedWsdlDocuments)
{
var referencedXmlSchemas = FindAllReferencedXmlSchemasRecursively(generatedWsdl, generatedXmlSchemas);
ClearWsdlOfExistingSchemas(generatedWsdl);
AddAllReferencedSchemas(generatedWsdl, referencedXmlSchemas);
}

RemoveSchemaLocationFromXmlSchemaImports(exporter, generatedXmlSchemas);
}


private static IEnumerable<XmlSchema> FindAllReferencedXmlSchemasRecursively(ServiceDescription wsdl, XmlSchemaSet generatedXmlSchemas)
{
var referencedXmlSchemas = new List<XmlSchema>();
foreach (XmlSchema schema in wsdl.Types.Schemas)
{
AddReferencedXmlSchemasRecursively(schema, generatedXmlSchemas, referencedXmlSchemas);
}
return referencedXmlSchemas;
}

/// <summary>
/// Recursively extract all the list of imported
/// schemas
/// </summary>
/// <param name="schema">Schema to examine</param>
/// <param name="generatedXmlSchemas">SchemaSet with all referenced schemas</param>
/// <param name="referencedXmlSchemas">List of all referenced schemas</param>
private static void AddReferencedXmlSchemasRecursively(
XmlSchema schema,
XmlSchemaSet generatedXmlSchemas,
List<XmlSchema> referencedXmlSchemas
)
{
foreach (XmlSchemaImport import in schema.Includes)
{
ICollection realSchemas = generatedXmlSchemas.Schemas(import.Namespace);
foreach (XmlSchema ixsd in realSchemas)
{
if (!referencedXmlSchemas.Contains(ixsd))
{
referencedXmlSchemas.Add(ixsd);
AddReferencedXmlSchemasRecursively(ixsd, generatedXmlSchemas, referencedXmlSchemas);
}
}
}
}

private static void ClearWsdlOfExistingSchemas(ServiceDescription wsdl)
{
wsdl.Types.Schemas.Clear();
}

private static void AddAllReferencedSchemas(ServiceDescription wsdl, IEnumerable<XmlSchema> referencedXmlSchemas)
{
foreach (XmlSchema schema in referencedXmlSchemas)
{
wsdl.Types.Schemas.Add(schema);
}
}

private static void RemoveSchemaLocationFromXmlSchemaImports(WsdlExporter exporter, XmlSchemaSet schemaSet)
{
var mySchemaSet = new XmlSchemaSet();
mySchemaSet.Add(schemaSet);
foreach (XmlSchema schema in mySchemaSet.Schemas())
{
exporter.GeneratedXmlSchemas.Remove(schema);
}
}

4 comments:

Tal said...

Hi,
How do I declare the service to use our custom exporter using the web config if my service is running on a web application?

Rune Sundling said...

Hi,

"running on a web application"? Can you add some more details?

Not hosted on iis?

Livingston Seagull said...

Hi Rune,

I attended your talk at XP2010 - Being able to code doesn't make you a good developer. I really liked it and was wondering if you could share the slides of your talk. I tried to access them via the XP2010.org website but was unsuccessful.

Thanks

Livingston Seagull said...

Hi Rune,

I found the slides on slideshare!

Thanks for posting them there!

Cheers