Reputation: 189
I am trying to integrate a Java web service into my C# project. The web service is maintained by another division of my company and it is out of my control.
I have created a service reference and I can call the service. The problem is that all of the response object's properties are null. The SOAP response is coming back from the server, but it is not getting deserialized properly. There are no errors reported or logged.
This appears to be a fairly common problem and the common answer is that there is a namespace mismatch. I believe this is my problem as well, but I am having trouble getting it corrected.
The web service is private. I have tried to put together a sample that illustrates the problem. Here is the relevant Reference.cs code:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "www.namespace1.com", ConfigurationName = "MyService.MyServiceClient")]
public interface MyServiceClient
{
[System.ServiceModel.OperationContractAttribute(Action = "", ReplyAction = "*")]
[System.ServiceModel.XmlSerializerFormatAttribute(Style = System.ServiceModel.OperationFormatStyle.Rpc, SupportFaults = true, Use = System.ServiceModel.OperationFormatUse.Encoded)]
[System.ServiceModel.ServiceKnownTypeAttribute(typeof(MyOperationResponseType))]
myOperationResponse myOperation(myOperationRequest request);
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.SoapTypeAttribute(Namespace = "www.namespace2.com")]
public partial class MyOperationResponseType : object, System.ComponentModel.INotifyPropertyChanged
{
private bool callSuccessfulField;
private string callErrorMessageField;
/// <remarks/>
public bool CallSuccessful
{
get
{
return this.callSuccessfulField;
}
set
{
this.callSuccessfulField = value;
this.RaisePropertyChanged("CallSuccessful");
}
}
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(IsNullable = true)]
public string CallErrorMessage
{
get
{
return this.callErrorMessageField;
}
set
{
this.callErrorMessageField = value;
this.RaisePropertyChanged("CallErrorMessage");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.MessageContractAttribute(WrapperName = "myOperationResponse", WrapperNamespace="www.namespace1.com", IsWrapped = true)]
public partial class myOperationResponse
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace = "", Order = 0)]
public MyOperationResponseType result;
public myOperationResponse()
{
}
public myOperationResponse(MyOperationResponseType result)
{
this.result = result;
}
}
The SOAP response that I'm getting looks like this:
<env:Envelope env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<m:myOperationResponse xmlns:m="www.namespace1.com">
<result>
<ns2:CallSuccessful xmlns:ns2="www.namespace2.com">false</ns2:CallSuccessful>
<ns2:CallErrorMessage xmlns:ns2="www.namespace2.com">Failed for some reason.</ns2:CallErrorMessage>
</result>
</m:myOperationResponse>
</env:Body>
</env:Envelope>
An instance of MyOperationResponseType gets created, but CallErrorMessage and CallSuccessful fields are not populated.
I used SGEN to generate the XML serialization classes and found that it is a namespace issue. The following code excerpts are from the deserialzer:
string id2_Item;
id2_Item = Reader.NameTable.Add(@"");
global::MyOperationResponseType o;
o = new global::MyOperationResponseType();
if (!paramsRead[1] && ((object) Reader.LocalName == (object)id158_CallSuccessful && (object) Reader.NamespaceURI == (object)id2_Item)) {
{
o.@CallSuccessful = System.Xml.XmlConvert.ToBoolean(Reader.ReadElementString());
}
paramsRead[1] = true;
}
The conditional is failing because Reader.NamespaceURI = "www.namespace2.com"
and id2_Item = ""
If I manually set id2_Item = "www.namespace2.com"
then the deserialization works.
Also, if I strip the namespaces out of the response SOAP, it will work:
<env:Envelope env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<m:myOperationResponse xmlns:m="www.namespace1.com">
<result>
<CallSuccessful>false</CallSuccessful>
<CallErrorMessage>Failed for some reason.</CallErrorMessage>
</result>
</m:myOperationResponse>
</env:Body>
I have the workarounds:
However, I would like to find a way to make this work without the workarounds. I just can't seem to find a way to get the namespaces to match up. I have tried a variety of different attributes, but nothing seems to make much of a difference.
I have not posted the WSDL. It is a private service and I can't make any changes to it. If you need to see the WSDL, let me know and I'll see what I can do.
Upvotes: 4
Views: 4447
Reputation: 1314
Most likely the WSDL does not have the CallSuccessful and CallErrorMessage tags with the correct namespaces, which is why your service proxy class doesn't have the appropriate namespace.
You can actually download the WSDL, save it in your project, deal with the namespace how you need, generate the service reference from this WSDL (either by using the GUI wizard or svcutil), point the app.config or web.config file to the actual java web service. You can follow this process everytime you need to update the service.
Hope this helps.
Upvotes: 1