In my web service I want to get rid of the generated ArrayOf... definitions in the WSDL which are generated by calling ...service.svc?singleWsdl.
Currently the definiton looks like (and I tried all varieties using XmlArray, etc.):
[DataContract]
public class Incident {...}
[CollectionDataContract(Name = "IncidentList", ItemName = "IncidentItem")]
public class IncidentList : List<Incident>
{
public IncidentList()
: base()
{ }
public IncidentList(IEnumerable<Incident> list)
: base(list)
{ }
}
[MessageContract]
public class IncidentsResponse
{
[MessageBodyMember]
public Incident[] Incidents { get; set; }
[MessageBodyMember]
public IncidentList IncidentList { get; set; }
}
When I get the WSDL I always receive (more or less - depending on Name attributes, etc.):
<xs:element name="IncidentsResponse">
<xs:complexType><xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="IncidentList" type="tns:ArrayOfIncident"/><xs:element minOccurs="0" maxOccurs="1" name="Incidents" type="tns:ArrayOfIncident"/>
</xs:sequence>
</xs:complexType></xs:element>
What I would really like to have is the types listed directly inside the element like
<xs:element name="IncidentsResponse">
<xs:complexType>
<Incidents><IncidentItem>...</IncidentItem><IncidentItem>...</IncidentItem> </Incidents>
<IncidentList><IncidentItem>...</IncidentItem><IncidentItem>...</IncidentItem></IncidentList>
</xs:complexType></xs:element>
So a reference to the actual data as such, not the list type (ArrayOf).
Any way to achieve this? The CollectionDataContract attribute is supposed to do the trick if I get the info right, but somehow it doesn't...
Especially as the consuming client is Java this extra indirection is hurting a bit as it bloats the code
Any ideas welcome :-)
this is intended since wcf can be used across language so it should support common types rather than only supporting .net specific type.
still if u want to generate a proxy using svcutil with list then you can create it by using the sample svcutil wsdl /ct:System.Collections.Generic.List`1 or if you are using adding service reference then there is an advanced button , Click that button and select the System.Generic.List as your Collection. This will resolve the problem.. and having the array information will not harm anything so it can be left as it is.
Related
how to set attributes for soap message:
for example my soap messg look like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity>
<a:codeField>Payment Request Successful</a:codeField>
<a:textField i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
but i need a soap message which take attributes not elements
like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity codeField="Payment Request Successful">
some text here
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
I am using datacontract in class.
It’s a common problem – you want to return an object from a WCF service as XML, but you either want, or need, to deliver some or all of the property values as XML Attributes instead of XML Elements; but you can’t because the DataContractSerializer doesn’t support attributes (you’re most likely to have seen this StackOverflow QA if you’ve done a web search). Most likely you’ve then migrated all your WCF service code to using the XmlSerializer (with all the XmlElement/XmlAttribute/XmlType attributes et al) – and you’ve cursed loudly.
Well, I’m here to rescue you, because it is possible – and the answer to the problem is actually inferred from the MSDN article entitled ‘Types supported by the Data Contract Serializer’.
The example I’m going to give is purely for illustration purposes only. I don’t have a lot of time, so work with me!
•Create a new Asp.Net WCF service application, you can use Cassini as your web server (probably easier – otherwise you might have to enable Asp.Net compatibility mode).
•Open the web.config and delete the element that was created for the new service.
•The interface and implementation model for this example is overkill. Move the [ServiceContract] and [OperationContract] declarations from the interface that was created for you new service to the class that was also created. Delete the interface.
•Open the .svc markup file and add the following at the end: Factory="System.ServiceModel.Activation.WebServiceHostFactory" – this enables the zero-configuration WCF model for this service (we’re going to create a RESTful service).
•Paste the following class declarations into your svc codebehind:
public interface IExampleData
{
string Description { get; set; }
string Name { get; set; }
int ID { get; set; }
}
public class ExampleData : IExampleData
{
public string Description { get; set; }
public string Name { get; set; }
public int ID { get; set; }
}
public class ExampleDataAttributed : ExampleData, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//implement if remote callers are going to pass your object in
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("id", ID.ToString());
writer.WriteAttributeString("name", Name);
//we'll keep the description as an element as it could be long.
writer.WriteElementString("description", Description);
}
#endregion
}
Just to demonstrate the point, the class that will be part-serialized to attributes simply derives from one that will be serialized as normal.
•Now add the following two methods to your service class:
[OperationContract]
[WebGet(UriTemplate = "/test1")]
public ExampleData Test1()
{
return new ExampleData() { ID = 1,
Name = "Element-centric",
Description =
"The contents of this item are entirely serialized to elements - as normal" };
}
[OperationContract]
[WebGet(UriTemplate = "/test2")]
public ExampleDataAttributed Test2()
{
return new ExampleData_Attributed() { ID = 2,
Name = "Mixed",
Description =
"Everything except this description will be serialized to attributes" };
}
Cover, and bake for 40 minutes (that is – Build it).
If you left your service as Service1.svc, then run it and open up IE and browse to http://localhost:[port of cassini]/test1
The result should look something like this:
<JSLabs.ExampleData
xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Description>
The contents of this item are entirely serialized to elements - as normal
</Description>
<ID>
1
</ID>
<Name>
Element-centric
</Name>
</JSLabs.ExampleData>
Now browse to http://localhost:[port of cassini]/test2
<JSLabs.ExampleDataAttributed id="2" name="Mixed"
xmlns="http://schemas.datacontract.org/2004/07/JobServe.Labs.Web">
<description>Everything except this description will be
serialized to attributes</description>
</JSLabs.ExampleDataAttributed>
It’s made a little less impressive by that nasty ‘orrible “xmlns=” attribute that the WCF data contract serializer automatically puts on the type – but, as you can see, the ‘ID’ and ‘Name’ properties have indeed been pushed out as attributes!
We could have made both methods return IExampleData and then used the KnownType attribute on that interface in order to get it to support either (according to what the code of the methods returned).
To support deserializing an object from the attributes, all you have to do is to implement the IXmlSerializable.ReadXml method.
Finally, as the aforementioned MSDN article says about the supported types – you should also be able to use XmlElement/XmlNode types as a way of representing XML directly – the DataContractSerializer, like in this case, take the short route and simply gets the Xml.
This also shouldn’t affect JSON formatting if you’re dual-outputting objects for either XML or JSON clients.
Check the source of this article
We have a WCF service which includes Serializable classes in a contract that has DataContract and DataMember at the root level of the service.
While trying to build a solution to isolate the problem, I came across the following:
[ServiceContract]
public interface IService1
{
[OperationContract]
CompositeType GetDataUsingDataContract();
}
[DataContract]
public class CompositeType
{
[DataMember]
public MyType MyProperty { get; set; }
}
[Serializable]
public class MyType
{
private int amount1;
[XmlElement(Form = XmlSchemaForm.Unqualified, DataType = "int", ElementName = "AmountN")]
public int Amount1
{
get
{ return amount1; }
set
{ amount1 = value; }
}
}
Gives the following xsd:
<xs:complexType name="CompositeType">
<xs:sequence>
<xs:element name="MyProperty" type="tns:MyType" nillable="true" minOccurs="0"/>
</xs:sequence>
</xs:complexType><xs:element name="CompositeType" type="tns:CompositeType" nillable="true"/>
<xs:complexType name="MyType">
<xs:sequence>
<xs:element name="amount1" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:element name="MyType" type="tns:MyType" nillable="true"/>
</xs:schema>
Question is: Why is the private but not the public member being serialized?
Serializers and serialization attributes are two different things.
XmlElement is attribute for XmlSerializer but it has no meaning for DataContractSerializer or for BinaryFormatter. XmlElementAttribute Class
DataContractSerializer can serialize multiple types, but it uses own serialization algorithm Link. When serializing objects marked with [Serializable],
DataContractSerializer follows default serialization pattern (serializing all members, [NonSerialized] applies). If you need more control then you can implement ISerializable for custom serialization and then you can set node names and values in serialized object Serialization .
There is also an option to implement IXmlSerializable and have full control on how serialized object will look like.
This msdn article can explain, why wcf is able to serialize your MyType:
Types Supported by the Data Contract Serializer: . . .
Types marked with the SerializableAttribute attribute. Many types
included in the .NET Framework base class library fall into this
category. The DataContractSerializer fully supports this serialization
programming model that was used by .NET Framework remoting, the
BinaryFormatter, and the SoapFormatter, including support for the
ISerializable interface.
And since wcf have no problem to serialize private fields, probably this is the reason of serializing your privet field amount1.
INO, The question is "why your property Amount1 was not serialized?" I'd try to rename it (be different from field name), remove xml attributes on it, and re-try.
I have a question regarding WCF and deserialisation of XML.
Lets say i have an XSD that specifies a number of attributes as minoccurs='0'.
<xs:element name=TestData>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=0 name="stoppageHours>
<xs:simpleType>
<xs:restriction base="xs:int">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element minOccurs=0 name="stoppageDate>
<xs:simpleType>
<xs:restriction base="xs:dateTime">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:sequence>
<xs:complexType>
</xs:element>
If i dont have a value for one for the attributes i can omit the element tags in the XML i.e.
<TestData>
<StoppageHours>26</StoppageHours>
...omitted stoppageDate...
</TestData>
But, given that i have generated classes from the XSD to .NET c#, when i post the XML to my web service it complains that deserialisation has failed as it was expecting element ?
How can you force the deserialistion process to ignore the missing XML element and set the class memeber in question to a NULL value? I have tried setting the class members to system nullable types but to get this to work i have to pass the XML element as xsi:nillable? What i really want to be able to do is simply omit the XML tag.
Thanks.
The attribute that allows this functionatlity is called EmitDefaultValue. Your data contract on the service side will probably look something like this:
[DataContract]
public class TestData
{
[DataMember(EmitDefaultValue = false)]
public int StoppageHours { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime StoppageDate { get; set; }
}
When EmitDefaultValue is equal to false, it tells the data contract serializer in WCF to remove the elements from the input if their value is equal to the default value. For value types the serializer will on the server side set the value to it's default value if it's not present. So in this scenario if you pass
<TestData></TestData>
when it is received by WCF your object will have these values server side:
<TestData>
<StoppageHours>0</StoppageHours>
<StoppageDate>1/1/0001 12:00:00 AM</StoppageDate>
</TestData>
Edit: The other thing you may need to do based on your feedback comment is set the IsRequired attribute on the DataMember as well. When I tested this attribute should default to false but you can try and be explicit in the contract definition. The updated contract would now look like this:
[DataContract]
public class TestData
{
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public int StoppageHours { get; set; }
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public DateTime StoppageDate { get; set; }
}
When the value is set to true and the nodes are missing I get a serialization exception. When IsRequired is set to false I can send an empty request:
<TestData></TestData>
and there is no serialization exception. If this doesn't work then you may need need to clear your temporary asp.net files and restart the service. I had that issue when testing this out. Hope this helps.
Fairly new to WCF and need help with understanding why serialisation is not working correctly.
Service definition - I just want to post, serialise into a LogDeviceCommunication object and then just return the object as a simple test
[OperationContract]
[WebInvoke(UriTemplate = "AddDeviceCommunicationLog", RequestFormat =
WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, Method = "POST")]
LogDeviceCommunication AddDeviceCommunicationLog(LogDeviceCommunication
deviceCommunicationEntry);
public LogDeviceCommunication AddDeviceCommunicationLog(LogDeviceCommunication
deviceCommunicationEntry)
{
return deviceCommunicationEntry;
}
At the moment I am just posting the following XML with Fiddler as a test.
<?xml version="1.0" encoding="UTF-8"?>
<LogDeviceCommunication>
<ID>1207a26e-ab59-4977-b7eb-b2776205cffe</ID>
<DeviceID>A42E8707-7C65-45AA-8E58-5D21F53DA101</DeviceID>
<Time>2012-03-14T15:38:28.379Z</Time>
<Line>0</Line>
<Tab>0</Tab>
<Info>Starting Synchronisation</Info>
</LogDeviceCommunication>
Results returned from Fiddler
<LogDeviceCommunication z:Id="i1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<ChangeTracker z:Id="i2"
xmlns:a="http://schemas.datacontract.org/2004/07/conxEntities">
<a:ExtendedProperties/>
<a:ObjectsAddedToCollectionProperties/>
<a:ObjectsRemovedFromCollectionProperties/>
<a:OriginalValues/>
<a:State>Added</a:State>
</ChangeTracker>
<DeviceID>00000000-0000-0000-0000-000000000000</DeviceID>
<ID>1207a26e-ab59-4977-b7eb-b2776205cffe</ID>
<Info i:nil="true"/>
<Line i:nil="true"/>
<Tab i:nil="true"/>
<Time>2012-03-14T15:38:28.379Z</Time>
</LogDeviceCommunication>
Why does the DeviceID contain the 0000's (I assume it's a null Guid) while the ID contains the correct Guid; also why do the Info, Line and Info elements contain nil values?
The LogDeviceCommunication is a POCO generated from EF4 using the ADO.NET Self Tracking Template
Condensed version is
[DataContract(IsReference = true, Namespace = "")]
public partial class LogDeviceCommunication: IObjectWithChangeTracker,
INotifyPropertyChanged
[DataMember]
public System.Guid DeviceID
[DataMember]
public System.DateTime Time
[DataMember]
public Nullable<int> Line
[DataMember]
public Nullable<int> Tab
[DataMember]
public string Info
[DataMember]
public System.Guid ID
I am sure I am doing something incorrectly so any help appreciated.
The problem lies in the required ordering of the XML.
WCF Datacontract, some fields do not deserialize
http://neimke.blogspot.co.nz/2012/03/serialization-ordering-causes-problems.html
When WCF receives your request, its deserialization machinery will create a new instance of the LogDeviceCommunication type to populate with the values it receives. It seems that the code from the EF partial class of your instance is being triggered and it results in what you post in your question.
Try setting a debugger break point on the return statement in your AddDeviceCommunicationLog method to see what EF & WCF deserialized for you. If it's just as what you posted then the issue is likely caused by the EF plumbing code. Also, you may want to enable WCF message tracing to see what WCF is actually receiving and sending back.
EDIT: Just ran across this blog post that shows some of the interaction between EF & WCF. You may want to review it to see if it's applicable to your issue.
I bet the other parts of the class, generated by that template, include the elements you're seeing.
In general, it's not a good idea to return EF entities (or any complex .NET type) from a web service - they drag along implementation dependencies. Return a purely POCO class as a DTO instead.
I need to have 2 families of classes (one on server and one on client side) which are identical in data structure but differs in behavior. Also I suppose that these fmailies will be enough big, thus I don't want to implement intermediate level of DTO and transformations into and from it.
I decided to move in following manner: declare shared assembly with declaration of data and services interfaces like these ones:
public interface ITest
{
string Title { get; set; }
int Value { get; set; }
}
public interface IService
{
ITest GetData();
}
Having these declarations I can implement these interfaces on server side for example basing on Entity Framework (data) and WCF (services). On the client side I can use for example Dependency Properties (data) and WCF (service).
When I started trying to implement this, I met several troubes.
First one was about server side of WCF - it simply do not want to work with interfaces as return parameters. Thanks to StackOverflow this issue was resolved like here
.
Next problem is that XML rendered by server side includes qulified assembly name of serialized on the server class.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataResponse xmlns="http://tempuri.org/">
<Test z:Id="1" z:Type="Dist.Server.Model.Test" z:Assembly="Dist.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/Dist.Server.Model" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Title z:Id="2">Test</Title>
<Value>123</Value>
</Test>
</GetDataResponse>
</s:Body>
</s:Envelope>
Thus during deserialization on client side there was an attempt to load this type. As this type is inaccessible on client side, I had to implement some kind of type mapping. I found that this is quite easy as NetDataContractSerializer used for serialization supports Binder property. Thus I override this property on client side and return correct value (hardcode in meantime, but it's OK for tests).
public class NetBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName) {
var type = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
return type;
}
}
Now I have following picture:
- Server uses NetDataContractSerializer to serialize response. It uses actual value (calss) during serialization instead of type used in declaration of service (interface).
- Client side receives XML and starts deserialization. To resolve type, NetDataContractSerializer calls my Binder that returns correct type.
- NetDataContractSerializer creates instance of correct type and starts loading of its properties.
And here I got a trouble that I don't know how to resolve. Values of properties are not deserialized. It means that instance of class is created correctly (uninitialized instance created through reflection services), but all properties are in their default values (0 or null).
I tried to play with declaration of class on client side: mark it as [Serializable], implement ISerializable, etc., but nohing is helpful. NetDataContractSerializer requires class to be marked as [DataContract] or [Serializable]. First option leaves properties empty, second one causes exceptions like "</Test> is unexpected, expected is bla-bla-bla_Value_Ending_bla-bla-bla".
Does anybody have any suggestions on how to resolve this last step?
I can provide full sources for better understanding, but I don't know ifI can attach them here...
Thanks in advance.
You could have a look at frameworks like AutoMapper that would take care the transformation to and from DTO. This would make your life much easier.
Instead of using an interface why not create a base class containing only the data and inherit it on both sides by sharing the assembly containing this class. Playing around with the ServiceKnownType should help you fix the last issues.
You may also share the same base classes on both sides and implement the specific logic as extension methods.
Seems that problem was solved enough easily. I created own serializer and used it instead of NetDataContractSerializer. Code is quite simple:
public class MySerializer: XmlObjectSerializer
{
public override void WriteStartObject(XmlDictionaryWriter writer, object graph) {
}
public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) {
var formatter = new XmlSerializer(graph.GetType());
formatter.Serialize(writer, graph);
}
public override void WriteEndObject(XmlDictionaryWriter writer) {
}
public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) {
var realType = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //temporary solution
var formatter = new XmlSerializer(realType);
return formatter.Deserialize(reader);
}
public override bool IsStartObject(XmlDictionaryReader reader) {
return true;//temporary solution
}
}
I checked SOAP that goes from server to client and it's almost the same as NetDataSerializer renders. The only difference is in attribute xmlns="".
Kai, Johann thanksfor your tries.