I am consuming a SOAP service, and successfully receiving a response.
Unfortunately, WCF isn't deserialising the members of the response POCO.
Using Diagnostics Tracing, I get the following error occurring for each member in the Response:
Description An unrecognised element was encountered in the XML during deserialisation which was ignored.
Element :partnerId
Please can you see where I am going wrong?
Here's the code:
Service Contract
[ServiceContract(Namespace = "https://foo.com/services/mediationV1/", ConfigurationName = "IPlatformPortType")]
public interface IPlatformPortType
{
[OperationContract(Action = "https://foo.com/services/foo_bar_HeartBeatV1/", ReplyAction = "*")]
HeartBeatResponse SendHeartBeat(HeartbeatRequest request);
}
POCO Request
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatRequest",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartbeatRequest
{
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 0)]
public string transactionId { get; set; }
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 2)]
public string partnerId { get; set; }
}
SOAP Request
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">https://foo.com/services/foo_bar_HeartBeatV1/</a:Action>
<a:MessageID>urn:uuid:73efa49e-f2b3-4f5d-b750-6784b1becfbc</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<foo_bar_HeartBeatRequest xmlns="https://foo.com/schemas/fooV1/">
<transactionId>f642df34-d328-4d76-9a9f-3cf64bdef71d</transactionId>
<partnerId>bob</partnerId>
</foo_bar_HeartBeatRequest>
</s:Body>
</s:Envelope>
POCO Response
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatResponse",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartBeatResponse
{
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 2)]
public string transactionId { get; set; }
[MessageBodyMember(Namespace = "https://foo.com/schemas/fooV1/", Order = 3)]
public int requestStatus { get; set; }
}
SOAP Response
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:MessageID>urn:uuid:b66497fd-4e95-458c-8772-c8bfc0decb51</wsa:MessageID>
<wsa:Action>urn:mediateResponse</wsa:Action>
<wsa:RelatesTo>urn:uuid:73efa49e-f2b3-4f5d-b750-6784b1becfbc</wsa:RelatesTo>
</soapenv:Header>
<soapenv:Body>
<ns:foo_bar_HeartBeatResponse xmlns:ns="https://foo.com/schemas/fooV1/">
<transactionId>f642df34-d328-4d76-9a9f-3cf64bdef71d</transactionId>
<requestStatus>1</requestStatus>
</ns:foo_bar_HeartBeatResponse>
</soapenv:Body>
</soapenv:Envelope>
Update
As per #TomRedfern's comment, I tried swapping out all the MessageContract attributes with DataContract.
Unfortunately this takes away my ability to notify the seriliaser that the POCO is a wrapper, causing the SOAP to look like the following:
<s:Body>
<SendHeartBeat xmlns="https://foo.com/services/fooV1/">
<request xmlns:b="https://foo.com/schemas/fooV1/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<b:transactionId>7085587a-4275-49ad-9aa9-5b413afcbe62</b:transactionId>
<b:partnerId>bob</b:partnerId>
</request>
</SendHeartBeat>
</s:Body>
Notice the name of the node is now the name of the Operation (SendHeartBeat), and it's child node is the parameter name of that operation.
I do not have control over the WSDL, so this will not work.
After a day's worth of trial and error attempts, I've finally got the deserialisation working correctly.
For each of the POCO's members I did the following:
add an XmlElementAttribute.
add the Form parameter to XmlElementAttribute, with value of XmlSchemaForm.Unqualified.
remove the Namespace parameter from the MessageBodyMemberAttribute and add to the XmlElementAttribute.
removed any existing parameters in MessageBodyMemberAttribute.
Here is the correct response POCO:
[MessageContract(IsWrapped = true,
WrapperName = "foo_bar_HeartBeatResponse",
WrapperNamespace = "https://foo.com/schemas/fooV1/")]
public class HeartBeatResponse
{
[MessageBodyMember]
[XmlElement(Form = XmlSchemaForm.Unqualified, Namespace = "https://foo.com/schemas/fooV1/")]
public string transactionId { get; set; }
[MessageBodyMember]
[XmlElement(Form = XmlSchemaForm.Unqualified, Namespace = "https://foo.com/schemas/fooV1/")]
public int requestStatus { get; set; }
}
Related
Is there way to change the wcf contract from.
This
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://schemas.datacontract.org/2004/07/">
<soapenv:Header/>
<soapenv:Body>
**<AddCustomer>**
<!--Optional:-->
<Data>
<ns:AddressLine1>?</ns:AddressLine1>
<ns:AddressLine2>?</ns:AddressLine2>
<ns:City>?</ns:City>
<ns:Country>?</ns:Country>
<ns:Email>?</ns:Email>
<ns:FirstName>?</ns:FirstName>
<ns:HomePhone>?</ns:HomePhone>
<ns:ID>?</ns:ID>
<ns:LastName>?</ns:LastName>
<ns:MobilePhone>?</ns:MobilePhone>
<ns:State>?</ns:State>
<ns:Suburb>?</ns:Suburb>
</Data>
**</AddCustomer>**
To
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:biz="http://BizTalk.Test001.Customer">
<soapenv:Header/>
<soapenv:Body>
<biz:Data>
<ID>?</ID>
<FirstName>?</FirstName>
<LastName>?</LastName>
<MobilePhone>?</MobilePhone>
<HomePhone>?</HomePhone>
<Email>?</Email>
<AddressLine1>?</AddressLine1>
<AddressLine2>?</AddressLine2>
<Suburb>?</Suburb>
<City>?</City>
<State>?</State>
<Country>?</Country>
</biz:Data>
Basically i want to get rid of "AddCustomer" from the request. Can we do something using ServiceContract or OperationContract.
You have to implement IClientMessageInspector interface.So before sending a request or after receving the response you need to implement your own C# methods to override your request /response SOAP messages like,
public class ClientMessageInspector : IClientMessageInspector
{
public string LastRequestXml { get; private set; }
public string LastResponseXml { get; private set; }
public void AfterReceiveReply(ref Message reply, object correlationState)
{
LastResponseXml = reply.ToString();
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
LastRequestXml = request.ToString();
var xmlPayload = ChangeMessage();
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
writer.Write(xmlPayload);
writer.Flush();
ms.Position = 0;
var reader = XmlReader.Create(ms);
request = Message.CreateMessage(reader, int.MaxValue, request.Version);
var payload = request.ToString();
return request;
}
/// Manipulate the SOAP message
private string ChangeMessage()
{
//manipulation code here to remove AddCustomer.
}
}
For detail implementation check out this blog.
https://cmatskas.com/changing-soap-message-data-and-namespaces-with-c/
I am building a SOAP Server with WCF in C#.
I have an Request from a Client thats looks like that :
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<sendLocalListRequest xmlns="urn://Ocpp/Cp/2012/06/">
<updateType>Full</updateType>
<listVersion>5678</listVersion>
<localAuthorisationList>
<idTag>1111111</idTag>
<idTagInfo>
<status>Accepted</status>
<expiryDate>2015-10-27T00:00:00</expiryDate>
<parentIdTag>555</parentIdTag>
</idTagInfo>
</localAuthorisationList>
<localAuthorisationList>
<idTag>2112432</idTag>
<idTagInfo>
<status>Accepted</status>
<expiryDate>2015-10-29T00:00:00</expiryDate>
<parentIdTag>555</parentIdTag>
</idTagInfo>
</localAuthorisationList>
<localAuthorisationList>
<idTag>44444444</idTag>
<idTagInfo>
<status>Accepted</status>
<expiryDate>2015-10-29T00:00:00</expiryDate>
<parentIdTag>2222</parentIdTag>
</idTagInfo>
</localAuthorisationList>
<hash>bhghs77767676777</hash>
</sendLocalListRequest>
I have to get this Request and store the Data between the Elements localAuthorisationList in a file on harddisk.
I made an [MessageContract] that Looks :
[MessageContract(IsWrapped = true, WrapperName = "sendLocalListRequest",
WrapperNamespace = "urn://Ocpp/Cp/2012/06/")]
public class sendLocalListRequest
{
[MessageBodyMember(Order=1)]
public UpdateType updateType;
[MessageBodyMember(Order=2)]
public int listVersion;
[MessageBodyMember(Order=3)]
public localAuthorisation[] localAuthorisationList;
[MessageBodyMember(Order=4)]
public string hash;
}
[DataContract(Namespace = "urn://Ocpp/Cp/2012/06/")]
public class localAuthorisation
{
[DataMember(IsRequired=true, Name = "idTag", Order = 1)]
public string idTag;
[DataMember(Name="idTagInfo", Order=2)]
public Data idTagInfo;
}
[DataContract(Namespace = "urn://Ocpp/Cp/2012/06/")]
public class Data
{
[DataMember(Name = "status", Order=1)]
public string Status;
[DataMember(Name = "expiryDate", Order=2)]
public DateTime ExDate;
[DataMember(Name = "parentIdTag", Order = 3)]
public string parentTag;
}
But with the WCFTestClient I get the following Request :
<s:Body>
<sendLocalListRequest xmlns="urn://Ocpp/Cp/2012/06/">
<updateType>Differential</updateType>
<listVersion>0</listVersion>
<localAuthorisationList xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<localAuthorisation>
<idTag>sfdsdgfg</idTag>
<idTagInfo>
<ExDate>2015-11-02T16:37:00</ExDate>
</idTagInfo>
</localAuthorisation>
<localAuthorisation />
</localAuthorisationList>
<hash i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
</sendLocalListRequest>
</s:Body>
My problem is, that I have an element too much (localAuthorisationList or localAuthorisation). Can I eliminate one Element? And how?
Thank you for your help
Tom
I have a WCF method which currently returns a string like so:
string CreateDesign(string UID, string TemplateName)
The retruned string is created by this method:
string RequestProcessed(string status, string UID)
{
XDocument doc = new XDocument(
new XElement("RequestProcessed",
new XElement("Status", status),
new XElement("UID", UID)));
return doc.ToString();
}
The response that I got from using SOAPUI is like this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<CreateDesignResponse xmlns="http://tempuri.org/">
<CreateDesignResult><![CDATA[<RequestProcessed>
<Status>OK</Status>
<UID>FolderName</UID>
</RequestProcessed>]]></CreateStandardResult>
</CreateStandardResponse>
</s:Body>
</s:Envelope>
The client is expecting it like this:
<soap:Body>
<m:RequestProcessed xmlns:m=" ">
<m:Status></m:Status>
<m:UID></m:UID>
</m: RequestProcessed >
</soap:Body>
What should I do to make the WCF service return the response like that?
I am struggling with this for quite sometime now, appreciate any help.
Regards.
To have full controll over your SOAP message body you should use MessageContract attribute for both your input and output parameters. Here is a good article on the web to start with.
Using Message Contracts
Hope it helps!
EDITED:
Operation contract
[ServiceContract]
public interface ITestService
{
[OperationContract]
RequestProcessed TestMethod(RequestInput tt);
}
RequestProcessed
[MessageContract]
public class RequestProcessed
{
[MessageBodyMember]
public string Status { get; set; }
[MessageBodyMember]
public Guid UID { get; set; }
}
SOAP message body
<s:Body>
<RequestProcessed xmlns="http://tempuri.org/">
<Status>OK</Status>
<UID>ffd338ed-bca3-46c1-9ee3-3c92ba3b3acc</UID>
</RequestProcessed>
</s:Body>
I'm trying to create a WCF SOAP service that has a service method that accepts bare parameters in the body but I just cant make it happen. At the moment, the method name element is being created under the body. I'm trying to use ws-addressing so that the method name is part of the header and the parameters are the direct children of the body.
Here's my service implementation:
[SoapDocumentService(Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public class Service1 : IService1
{
[SoapDocumentMethod(Use=SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Bare)]
public void DoWork([XmlElement(Namespace = "http://www.contoso.com",
IsNullable = true)] MyClass wrapper)
{
}
}
[XmlRoot(Namespace = "http://www.contoso.com")]
public class MyClass
{
public int Value { get; set; }
public string MyProperty { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
void DoWork(MyClass wrapper);
}
The above implementation generates the soap client below. But I'm trying to have the wrapper element as the direct child on body (trying to remove DoWork). From what I've read, decorating the svc method to use bare parameters should remove the service method name (DoWork) and use ws-addressing.
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:web="http://schemas.datacontract.org/2004/07/WebApplication2">
<soap:Header/>
<soap:Body>
<tem:DoWork> <!-- I want to remove this svc method name element -- >
<tem:wrapper> <!-- I want this to be under the body -->
<!--Optional:-->
<web:MyProperty>?</web:MyProperty>
<!--Optional:-->
<web:Value>?</web:Value>
</tem:wrapper>
</tem:DoWork>
</soap:Body>
</soap:Envelope>
I've followed the guide from msdn to decorate the service method. MSDN Link
I think you should drop the wrapper. in .net 2 this would work, wcf should be similar:
[WebMethod]
[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]
public String EchoString(String s, String s1)
{
return s;
}
I had to create message contracts wrapper for the MyClass and specify the message body.
[MessageContract]
public class MyWrapper
{
[MessageBodyMember]
public int Value { get; set; }
[MessageBodyMember]
public string MyProperty { get; set; }
}
I am sharing contract between my client and server (not using generated proxies). Here is the contract (note I have also tried ObservableCollection):
public class MyList : List<MyItem> {
public int Total { get; set; }
public MyList() { }
}
The Total is still set when the EndMyOperation async callback method is called on the server. However, when I look at the value on the client the Total is gone (set to 0). The trace clearly shows that the additional attribute is not being passed. Can anyone shed some light on why this is happening?
<MessageLogTraceRecord>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/MyOperation</Action>
<ActivityId CorrelationId="d2872ac2-685e-4f94-b516-aaba4effa463" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">695d611f-2b3d-48ca-80b8-7ceae4fd423f</ActivityId>
</s:Header>
<s:Body>
<MyOperationResponse xmlns="http://tempuri.org/">
<MyOperationResult xmlns:d4p1="http://schemas.datacontract.org/2004/07/MyDataContract" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:MyItem>
<d4p1:MyId>1</d4p1:MyId>
</d4p1:MyItem>
<d4p1:MyItem>
<d4p1:MyId>12</d4p1:MyId>
</d4p1:MyItem>
</MyOperationResult>
</MyOperationResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
Just to clarify I know I can solve the issue, by not subclassing and doing something like below. Rather I am trying to understand why the above behavior happens.
public class MyPagedList<T> {
public int Total { get; set; }
public List<T> MyList { get; set; }
public MyPagedList() { MyList = new List<T>(); }
}
Is MyItem [Serializable]? Or rather are the fields on MyItem Serializable?
Or could this be your problem: inheriting-generic-collection-and-serialization