WCF deserialisation - wcf

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.

Related

How to get rid of ArrayOf in generated WSDL for MessageContract

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.

Why is the public property not being serialized?

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.

WCF Web Service xml serialization [XmlAttributeAttribute()] is ignored

I have a WCF web service developed with VS2010 targeting .Net Framework 4 which utilizes a serialization class generated from a xsd schema using xsd.exe.
When I request the xsd from the service (http://localhost:59120/Service1.svc?xsd=xsd2) the element attributes are ignored. E.G. in the schema snippet below -
<xs:element name="id"...>
should be an attribute -
<xs:attribute name="id"...>
Snippet from xsd request -
...
<xs:sequence>
<xs:element name="address" type="xs:string" nillable="true"/>
<xs:element name="emailId" type="xs:string" nillable="true"/>
<xs:element name="id" type="xs:string" nillable="true"/>
<xs:element name="items" type="tns:ArrayOfArrayOfOrdersCustomerItemsItem" nillable="true"/>
<xs:element name="name" type="xs:string" nillable="true"/>
</xs:sequence>
...
For some reason the statement "[XmlAttributeAttribute()]" in my class is being ignored I've tried changing it to [XmlAttribute()] and [XmlAttributeAttribute("Id")] and removing the line altogether but it makes no difference at all.
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[XmlTypeAttribute(AnonymousType = true)]
public partial class OrdersCustomer
{
/// <remarks/>
[XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 0)]
public string Name;
/// <remarks/>
[XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 1)]
public string Address;
/// <remarks/>
[XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 2)]
public string EmailId;
/// <remarks/>
[XmlArrayAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified, Order = 3)]
[XmlArrayItemAttribute("Item", typeof(OrdersCustomerItemsItem), Form = System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable = false)]
public OrdersCustomerItemsItem[][] Items;
/// <remarks/>
[XmlAttributeAttribute()]
public string Id;
}
Make sure that your [ServiceContract] interface has the [XmlSerializerFormat] attribute. If this is not present, then WCF will use the DataContractSerializer, and all your XmlSerializer attributes will be ignored.

Why is using [DataMember(EmitDefaultValue = false)] not recommended?

In WCF you can define a contract using the [DataContract] and [DataMember] attributes, like this:
[DataContract]
public class Sample
{
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public string Test { get; set; }
}
This article on the MSDN states that using EmitDefaultValue = false is not recommended:
However, i like to use this, because the XML that is generated using this construction is cleaner. Not specifying this setting results in:
<Sample>
<Test xsi:nil="true"/>
</Sample>
while using the setting the element is ommited when there is no value:
<Sample>
</Sample>
I'm curious to what the reasoning behind that statement is. Specifically since both snipptes of XML look equivalent to me (and both last part can be deserialized correctly for this contract).
What is the reasoning behind this statement?
The reason is at the bottom of the article that you link to. The short version is:
When the EmitDefaultValue is set to false, it is represented in the schema as an annotation specific to Windows Communication Foundation (WCF). There is no interoperable way to represent this information. In particular, the "default" attribute in the schema is not used for this purpose, the minOccurs attribute is affected only by the IsRequired setting, and the nillable attribute is affected only by the type of the data member.
The actual default value to use is not present in the schema. It is up to the receiving endpoint to appropriately interpret a missing element.

How to expose enum attributes to WCF client

I want to expose enum attributes to WCF client application, but I can only see enum values.
Here is the enum:
public enum TemplateType
{
[EnumDescription("Property Particulars")]
[EnumValue("PropertyParticulars")]
PropertyParticulars = 1,
[EnumDescription("Short Format Lists")]
[EnumValue("ShortFormatLists")]
ShortFormatLists,
[EnumDescription("Client Letters")]
[EnumValue("ClientLetters")]
ClientLetters,
[EnumDescription("Labels")]
[EnumValue("Labels")]
Labels
}
How can I expose the Description and Value attributes?
You can expose enums from a service but the attributes on an enum are not serialized when they are sent over the wire. This means that consumers of this enum will only see the enum itself and none of your attributes.
What you need to do is dress up your enum with a DataContract attribute and the values with the EnumMember attribute so that your information will be serialized, but this will only allow you to specify the underlying value of each enum value, not a description.
There is a workaround if the intention is to expose a display text for enum members, define your enum in this way in the contracts:
public enum EPaymentCycle
{
[EnumMember(Value = "Month by Month")]
Monthly,
[EnumMember(Value = "Week by Week")]
Weekly,
[EnumMember(Value = "Hour by Hour")]
Hours
}
The SvcUtils serialization produces an interesting result:
public enum EPaymentCycle : int
{
[System.Runtime.Serialization.EnumMemberAttribute(Value="Month by Month")]
MonthByMonth= 0,
[System.Runtime.Serialization.EnumMemberAttribute(Value="Week by Week")]
WeekbyWeek= 1,
[System.Runtime.Serialization.EnumMemberAttribute(Value="Hour by Hour")]
HourbyHour = 2
}
You can read the EnumMemberAttribute Value by reflection and there you got it. Also the xsd metadata file produced by svcutil serialization is as expected:
<xs:simpleType name="EPaymentCycle">
<xs:restriction base="xs:string">
<xs:enumeration value="Month by Month" />
<xs:enumeration value="Week by Week" />
<xs:enumeration value="Hour by Hour" />
</xs:restriction>
I'm not fully versed in the specs, but I doubt this kind of metadata has an equivalent representation in WSDL. Thus, this will not be visible on the client side if you generate the types in your proxy.
However, if you put all your DataContracts in a separate assembly that you reference in the client, you can reuse those types on the client side. In that case, the attributes would be visible. "Reuse types in referenced assemblies" needs to be checked for your Service Reference, but this is on by default.
Here's a short blog post about it. I'm sure there are others...
Example enum for the values of a traffic light...
[DataContract]
public enum TrafficLightType
{
[EnumMember]
Red,
[EnumMember]
Green,
[EnumMember]
Amber
}