How to use a custom marshaller in spring WS - marshalling

I created a SOAP Webservice using spring-ws.
Here is my config Class :
#EnableWs
#Configuration
public class EmutationSOAPConfig extends WsConfigurerAdapter {
#Autowired
LoggingInterceptor loggingInterceptor;
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
return new ServletRegistrationBean(servlet, "/ws/*");
}
#Bean(name="emutationService")
public Wsdl11Definition defaultWsdl11Definition() {
SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
wsdl11Definition.setWsdl(new ClassPathResource("/wsdl/emutation-fc1.wsdl"));
return wsdl11Definition;
}
// ....
}
And here is my EndPoint class :
#Endpoint
public class EmutationServiceEndpoint {
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "ConsultationFibres" )
#ResponsePayload
public ConsultationFibresResponse consultationFibres(#RequestPayload ConsultationFibres request) throws SoapFaultClientException, EmutationDefaultFaultMessage, EmutationTechnicalFaultMessage {
ObjectFactory factory = new ObjectFactory();
ConsultationFibresResponse response =factory.createConsultationFibresResponse();
// ....
return response;
}
}
the types ConsultationFibresResponse and ConsultationFibres were generated from a WSDL that cannot be changed.
here is the WSDL part that describe ConsultationFibres :
<xsd:element name="ConsultationFibres">
<xsd:complexType>
<xsd:sequence>
<!-- .... -->
<xsd:element minOccurs="0" name="offset" type="xsd:int"/>
<xsd:element minOccurs="0" name="limit" type="xsd:int"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
the problem is that the elements offset and limit are unmarshalled as 0 when they are empty in xml SOAP request :
<ns2:offset></ns2:offset>
<ns2:limit></ns2:limit>
i want them to be null instead without modifying the WSDL or the generated classes.
I want to use the javax.xml.bind.annotation.adapters.XmlAdapter to define how to define how to convert string to integer.
but i don't know how to override the marshaller/unlarshaller used by spring.
please help me.

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.

WCF routing using XPath : invalid qualified name exception

I am trying to impelemnt content based routing using XPath in wcf.
I have create class library which contains service contract and data contract as following.
[ServiceContract(Namespace = "http://orders/")]
public interface IService5
{
[OperationContract]
string GetData(int value);
}
[DataContract]
public class Quantity
{
[DataMember]
public int value1 { get; set; }
}
I created one service as follows:
public class Service5 : IService5
{
public string GetData(int value)
{
return string.Format("You entered in service 5: {0}", value);
}
}
And I am trying to implement routing based on 'value'
In app.config (inside router project) i hav added following lines for namespace and XPath filter
<namespaceTable>
<add prefix="cc" namespace="http:orders/Quantity/"/>
</namespaceTable>
<filters>
<filter name="All" filterType="XPath" filterData="cc://value1 > 500 " />
But whenever i run the code i get an exception for ' cc://value1 > 500 ' as invalid qualified name exception.
How can i resolve this ?
There are multiple things wrong here:
The class Quantity on which you appear to want to apply the filter does not feature in your service contract at all, so will be entirely absent in the XML for filtering purposes.
The namespace in your router config starts http:orders, when the service contract namespace starts http://orders.
The namespace in your router config contains /Quantity, when the service contract namespace does not.
The filter xpath cc://value1 is not a valid xpath
---
Hey The problem is in the line
filter name="All" filterType="XPath" filterData="cc://value1 > 500 "
It should be
<filter name="All" filterType="XPath" filterData="//cc:value1 > 500 " />
observe cc:// in ur code.
This will solve ur problem

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.

WCF deserialisation

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.