Search code examples
javawcfsoapxsdwsdl

How to inject "catch-all" xsd:any element into every response object described by a WSDL?


We have a SOAP Web Service consumed both by C# and Java clients.

The problem is that clients do not hurry to regenerate their proxies when we upgrade the Web Service and hence a proxy code generated for an older version may be used to consume a newer version of the service.

It so happens, that adding a new property to a response object breaks the Java clients using the already generated old proxy code. Apparently, Java is very strict in its interpretation of WSDL and fails with an exception when a property arrives that does not exist in the WSDL. At least, this is what we observe with the proxy code generated by the apache axis. C# clients are fine - the new properties are simply ignored.

I am trying to figure out how to solve this problem. One solution which seems to be viable is to inject an xsd:any property into each and every response object found in the WSDL. From what I understand, the various Java implementations are going to use it as a "catch-all" property for all the unknown properties (of course, clients will have to regenerate their proxies to consume these xsd:any definitions, but once done new properties would seize to break their code)

The question is how can I sort of inject these xsd:any properties in the WSDL without actually adding real "catch-all" properties to the response objects?

Our Web Services are implemented using WCF.


Solution

  • Apparently, the only way to achieve it is by introducing a new endpoint behavior which also implements the IWsdlExportExtension interface. So, here we go:

    public class WSDLFilterBehavior : IWsdlExportExtension, IEndpointBehavior
    {
        public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
        {
            // never called
        }
    
        public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
        {
            var schemas = exporter.GeneratedXmlSchemas.Schemas(@"http://Abc/Services/Data");
            foreach (XmlSchema schema in schemas)
            {
                for (int i = schema.Items.Count - 1; i >= 0; --i)
                {
                    // Do some other processing ...
    
                    var schemaComplexType = schema.Items[i] as XmlSchemaComplexType;
                    if (schemaComplexType == null)
                    {
                        continue;
                    }
    
                    XmlSchemaSequence xmlSchemaSequence;
                    var xmlSchemaContentModel = schemaComplexType.ContentModel;
                    if (xmlSchemaContentModel == null)
                    {
                        if (schemaType.Name.StartsWith("ArrayOf"))
                        {
                            continue;
                        }
                        xmlSchemaSequence = schemaComplexType.Particle as XmlSchemaSequence;
                    }
                    else
                    {
                        var xmlSchemaComplexContentExtension = xmlSchemaContentModel.Content as XmlSchemaComplexContentExtension;
                        if (xmlSchemaComplexContentExtension == null)
                        {
                            continue;
                        }
    
                        xmlSchemaSequence = xmlSchemaComplexContentExtension.Particle as XmlSchemaSequence;
                    }
    
                    if (xmlSchemaSequence == null)
                    {
                        continue;
                    }
    
                    var xmlSchemaObject = new XmlSchemaAny
                    {
                        MinOccurs = 0
                    };
                    xmlSchemaSequence.Items.Add(xmlSchemaObject);
                }
            }
        }
    
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            // not needed
        }
    
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            // not needed
        }
    
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint,EndpointDispatcher dispatcher)
        {
            // not needed
        }
    
        public void Validate(ServiceEndpoint endpoint)
        {
            // not needed
        }
    }
    

    And it works, it injects the xs:any (not xsd:any, but I suppose it is the same) in all the elements, except those that do not derive from any other type and start with "ArrayOf".