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
Related
I enable the xml serializer in my .net core 5 web api:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddXmlSerializerFormatters()
//.AddXmlDataContractSerializerFormatters()
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.PropertyNamingPolicy = null;
o.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"Routing Evaluator Package Demo Service ({appSettings.Environment})", Version = "v1" });
});
}
My controller takes a parameter
[HttpPost]
public ActionResult<TestResponse> Post(TestRequest request)
{
...
}
And my model looks like this:
[Serializable]
//[DataContract]
public class TestRequest
{
/// <summary>
/// The message addressing information
/// </summary>
//[DataMember]
[XmlArray("Items")]
[XmlArrayItem("Item")]
public Items Items { get; set; }
}
[Serializable]
[XmlRoot("Items")]
//[CollectionDataContract(Name = "Items")]
public class Items : List<Item>
{
}
[Serializable]
//[DataContract]
[XmlRoot("Item")]
public class Item
{
public string key {get;set;}
public string value {get;set;}
}
If I manually serialize an instance of my object, I get results like this:
Object Instance:
var request = new TestRequest()
{
Items = new Items()
{
new Item() {key = "A", value="1"},
new Item() {key = "B", value="2"},
new Item() {key = "C", value="3"}
}
};
Serialization:
using (var str = new MemoryStream())
{
System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(DemoService.ServiceModels.EvaluationRequest));
ser.Serialize(str, request);
str.Flush();
str.Position = 0;
_output.WriteLine(str.ReadAsString());
}
Results:
<?xml version="1.0" encoding="utf-8"?>
<TestRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<Item>
<key>A</key>
<value>1</value>
</Item>
<Item>
<key>B</key>
<value>2</value>
</Item>
<Item>
<key>C</key>
<value>3</value>
</Item>
</Items>
</TestRequest>
If I browse to the swagger page, the example it shows is:
<?xml version="1.0" encoding="UTF-8"?>
<TestRequest>
<Items>
<key>A</key>
<value>1</value>
</Items>
</TestRequest>
The sample it supplied is incorrect. It appears that the serializer that swagger is using is not the same as the serializer that the web api is using as specfied by AddXmlSerializerFormatters. Do I need to configure swagger somehow to make them match?
Your model is wrong, it should be like below
[Serializable]
public class TestRequest
{
/// <summary>
/// The message addressing information
/// </summary>
//[DataMember]
[XmlArray("Items")]
[XmlArrayItem("Item")]
public List<Item> Items { get; set; }
}
[Serializable]
public class Item
{
public string key {get;set;}
public string value {get;set;}
}
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; }
}
I have developed WCF service using XmlSerializerFormat. When i test it in WCF Test client i get following response.
Response is wrapped under tag <AuthenticateUserResult>. But when service is consumed using client proxy I get response with out wrap. Can someone Please assist how can i avoid the response wrap or how can i get both responses match.
WCF Test client Response:
<AuthenticateUserResponse xmlns="http://tempuri.org/">
<AuthenticateUserResult>
<applicationStatus>Success</applicationStatus>
<authType version="3.0">
<authenticationType xmlns="http://aaaaaaaa.xyz">0</authenticationType>
</authType>
</AuthenticateUserResult>
</AuthenticateUserResponse>
Service Response from Client Proxy:
<AuthenticateUserResponse >
<applicationStatus xmlns="http://tempuri.org/">Success</applicationStatus>
<authType version="3.0" xmlns="http://tempuri.org/">
<authenticationType xmlns="http://aaaaaaaa.xyz">0</authenticationType>
</authType>
</AuthenticateUserResponse>
My sample application
Service contract:
[ServiceContract]
[XmlSerializerFormat]
public interface IService2
{
[OperationContract]
[XmlSerializerFormat]
AuthenticateUserResponse AuthenticateUser();
}
//[DataContract()]
public class AuthenticateUserResponse
{
[XmlElement("applicationStatus", IsNullable = false)]
public ApplicationStatusType ApplicationStatus
{
get;
set;
}
[XmlElement("authType")]
public AuthType AuthType
{
get;
set;
}
}
[XmlRoot(Namespace = "http://aaaaaaaa.xyz")]
public class AuthType
{
[XmlAttribute("version")]
public string Version;
[XmlElement("authenticationType")]
public int AuthenticationType
{
get;
set;
}
}
public enum ApplicationStatusType
{
Success = 0,
Failed = 0
}
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'm in the process of writing a WCF application which will be consumed by a Silverlight application. I have done most of the design work and I am now doing the implementation, which has made me come up with this question.
Here's an example of something that exists in my application:
[DataContract]
class Person
{
[DataMember]
private Towel mostRecentlyUsedTowel;
[DataMember]
private Gym gym; //the gym that this person attends
...
}
[DataContract]
class Gym
{
[DataMember]
private List<Towel> towels; //all the towels this gym owns
...
}
Here's what I'm getting at: In my application mostRecentlyUsedTowel will be pointing at something in the towels list for the person's gym. Some of my requests will serialize a Person object.
Is the DataContractSerializer smart enough to notice its being asked to serialize the exact same instance of an object twice? If so, how does it deal with it?
If it will just go about serializing the same instance twice, how should I deal with this so I'm not sending unnecessary data over the link?
The following code:
[TestMethod]
public void CanSerializePerson()
{
var towel1 = new Towel() { Id = 1 };
var towel2 = new Towel() { Id = 2 };
var towel3 = new Towel() { Id = 3 };
var gym = new Gym();
gym.towels.Add(towel1);
gym.towels.Add(towel2);
gym.towels.Add(towel3);
var person = new Person()
{
recentlyUsedTowel = towel1,
gym = gym
};
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb))
{
var ser = new DataContractSerializer(typeof (Person));
ser.WriteObject(writer, person);
}
throw new Exception(sb.ToString());
}
public class Person
{
public Towel recentlyUsedTowel { get; set; }
public Gym gym { get; set; }
}
public class Gym
{
public Gym()
{
towels = new List<Towel>();
}
public List<Towel> towels { get; set; }
}
public class Towel
{
public int Id { get; set; }
}
will evaluate to:
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org">
<gym>
<towels>
<Towel><Id>1</Id></Towel>
<Towel><Id>2</Id></Towel>
<Towel><Id>3</Id></Towel>
</towels>
</gym>
<recentlyUsedTowel><Id>1</Id></recentlyUsedTowel>
</Person>
If you added the IsReference property to the DataContract attribute of the Towel class like this:
[DataContract(IsReference=true)]
public class Towel
{
// you have to specify a [DataMember] in this because you are
// explicitly adding DataContract
[DataMember]
public int Id { get; set; }
}
you would get an output like this:
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org">
<gym>
<towels>
<Towel z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>1</Id>
</Towel>
<Towel z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>2</Id>
</Towel>
<Towel z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Id>3</Id>
</Towel>
</towels>
</gym>
<recentlyUsedTowel z:Ref="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" />
</Person>
Hope this Helps.