How to serialize a subclass using DataContracts - wcf

I just started programming a WCF project and there is a serialization issue I can't seem to fix. I'm using DataContracts and DataMembers to serialize my classes to XML.
I have a class TestResponse that includes a property TestParameter. This property contains one string parameter. Also, I have a subclass of TestParameter called TestParameterSubclass. I need to serialize this subclass. These three classes look like this:
TestResponse.cs
[DataContract(Namespace = Service.defaultNamespace, Name="TestResponse")]
public class TestResponse
{
[DataMember(Name = "TestParameter")]
public TestParameter param { get; set; }
}
TestParameter.cs
[DataContract(Namespace = Service.defaultNamespace, Name="TestParameter")]
[KnownType(typeof(TestParameterSubclass))]
public class TestParameter
{
[DataMember(Name="ParamName")]
public string ParamName {get; set;}
}
TestParameterSubclass.cs
[DataContract(Name="TestParameterSubclass")]
public class TestParameterSubclass : TestParameter
{
}
I have an OperationContract
[OperationContract]
[WebInvoke(
Method = "POST",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/calculateA")]
TestResponse CalculateA(TestRequest data);
and CalculateA
public TestResponse CalculateA(TestRequest request)
{
TestResponse tr = new TestResponse();
TestParameterSubclass parameterSubclass = new TestParameterSubclass();
tr.param = parameterSubclass;
tr.param.ParamName = "hai";
return tr;
}
which results in
<TestResponse>
<TestParameter i:type="a:TestParameterSubclass">
<ParamName>hai</ParamName>
</TestParameter>
</TestResponse>
However, I need the XML to use the subclass name as the element name, like this
<TestResponse>
<TestParameterSubclass>
<ParamName>hai</ParamName>
</TestParameterSubclass>
</TestResponse>
Does anyone know how to do this?
(I have a similar problem with deserializing the input XML, but I think both problems are related, so fixing this would (hopefully) also fix my other problem.)

Related

How do I overload method parameter using NET5 SoapCore

I'm trying to port an existing Soap WebService to .NET5 but am having issue with overloading a Soap method parameter.
In NET4 the code looks like this
namespace SoapWebServiceeTest.Soap
{
/// <summary>
/// Summary description for WsgSPServiceOrderService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class TextWebService : System.Web.Services.WebService
{
[WebMethod]
public string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request)
{
return $"{request.GetType().Name}";
}
}
public class Object1 { public string Param1 { get; set; } }
public class Object2 { public string Param2 { get; set; } }
public class Object3 { public string Param3 { get; set; } }
}
How do I achieve this in .NET5?
I have tried following but got reflection exception: System.Reflection.AmbiguousMatchException: 'Multiple custom attributes of the same type found.'
[ServiceContract]
public interface ITestWebService
{
[OperationContract]
string Test(
[XmlElement("object1", typeof(Object1))]
[XmlElement("object2", typeof(Object2))]
[XmlElement("object3", typeof(Object3))]
object request);
}
And also tried this but VS Add Connected Services errored with "More than one message named 'ISampleService_Test_InputMessage' was specified. Each message must have a unique name."
[OperationContract]
string Test(Object1 request);
[OperationContract]
string Test(Object2 request);
Any help would be awesome
You may try this
You can make it post or get based on your need
[OperationContract]
[WebInvoke(UriTemplate = "Test1", Method = "POST"]
string Test(Object1 request);
and
[OperationContract]
[WebInvoke(UriTemplate = "Test2", Method = "POST"]
string Test(Object2 request);
This way you can achieve objective

WCF Restful service parse enum within json cause error

I have a restful service based on WCF like below:
(The FeedbackInfo class has only one enum member - ServiceCode.)
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public List<FeedbackInfo> GetFeedbackInfoList(ServiceCode code)
{
return ALLDAO.GetFeedbackInfoList(code);
}
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public int? CreateFeedback(FeedbackInfo feedback)
{
return ALLDAO.CreateFeedback(feedback);
}
I will use jquery ajax to invoke these two method like below:
$.ajax({
type: "GET",
url: "/Service/ALL.svc/GetFeedbackInfoList",
datatype: "text/json",
data: { "code": "Info"},
});
var feedbackInfo = { feedback: {
ServiceCode: "Info"
}};
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Service/ALL.svc/CreateFeedback",
datatype: "text/json",
data: JSON.stringify(feedbackInfo),
});
The first calling will be excuted succesfully, whereas the second one give me an error: The value "Info" cannot be parsed as the type 'Int64'. I'm wondering why the same one enum can not be parsed in the second calling? Just because of the enum type being used as a member of class?
EDIT:
The FeedbackInfo and ServiceCode are looked like below:
public class FeedbackInfo
{
public int ID { get; set; }
public string Title { get; set; }
public ServiceCode ServiceCode { get; set; }
}
[DataContract]
public enum ServiceCode
{
[EnumMember]
Info,
[EnumMember]
Error
}
I have put together a better solution that uses the Newtonsoft.Json library. It fixes the enum issue and also makes the error handling much better. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
You have to add some entries to your Web.config to get it to work, you can see an example file here:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config
Enums are serialized as integers, so you'd need to use ServiceCode: 1 (or whatever), or alternatively, add a custom property in the FeedbackInfo class to deserialize the enum from a given string value. Ie, something like this:
public string ServiceCode {
get {
return ServiceCodeEnum.ToString();
}
set {
MyEnum enumVal;
if (Enum.TryParse<MyEnum>(value, out enumVal))
ServiceCodeEnum = enumVal;
else
ServiceCodeEnum = default(MyEnum);
}
}
private MyEnum ServiceCodeEnum { get; set; }

Passing List<string> in WCF Rest Post Method

I am trying to pass a List in WCF Post call as parameter. Below is my code.
TrackingNumbers.cs
[KnownType(typeof(List<string>))]
[DataContract]
public class TrackingNumbers
{
[DataMember]
public List<object> TrackingNumberList { get; set; }
}
IService.cs
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "v1/xml/GetMultipleShipmentDetails/")]
Shipments[] XMLMultipleData(TrackingNumbers trackingNumbers);
Now When I am passing the following XML from fiddler
<TrackingNumbers xmlns="http://schemas.datacontract.org/2004/07/Chhotu.Web.Partner.API">
<TrackingNumberList>10000008871</TrackingNumberList>
<TrackingNumberList>10000008864</TrackingNumberList>
<TrackingNumberList>10000008858</TrackingNumberList>
</TrackingNumbers>
the XMLMultipleData method is not getting called from debugger. Please suggest what i am doing wrong.
I see some issues with our code:
No need for [KnownType(typeof(List))], the DataContractSerializer knows this type already.
List - is not a good approach. If you use numbers why not make it an int?
Are you sure you are attached? You can try to enforce the attaching of a debugger with Debugger.Break(); at a good position in your code.
I will suggest you try change this line
Shipments[] XMLMultipleData(TrackingNumbers trackingNumbers);
with
Shipments[] XMLMultipleData(TrackingNumbers[] trackingNumbers);
And your [DataContract] as
[DataContract]
public class TrackingNumbers
{
[DataMember]
public int TrackingNumber{ get; set; }
}
Also! Try removing KnownType(typeof(List<string>))]
Thanks!

Return a WCF EF4 Entity as JSON

My service interface is:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "HelloJSON/{name}")]
string HelloJSON(string name);
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetEmployees")]
List<Employee> GetEmployees();
}
My implementation is:
public class MyService : IMyService
{
public string HelloJSON(string name)
{
return string.Format("Hello {0} in JSON", name);
}
public List<Employee> GetEmployees()
{
using (DBEntities ctx = new DBEntities())
{
List<Employee> emp = new List<Employee>();
emp = (from e in ctx.Employee select e).ToList();
return emp;
}
}
}
When I call the first method I get something like "Hello pepe in JSON", that's ok.
When I call the second method and set a breakpoint on line "return emp;" I get the list of the employees(there are 6 records from the database), but in IE I get this:
Internet Explorer cannot display the webpage
and testing in Firefox all I get is a blank page with a blank body, no HTML, no data and no errors.
I think WCF can't serialize my default EF4 entities.
EDIT:
My final solution was something(not exactly) like this:
static string SerializeJSON<T>(T obj) {
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj); }
EF entities cannot be serialized by default you must add code generation to them.
Refer to this article on how to create Serializable entities.
called Self Tracking entities
The best way would be to enable RIA Services and expose JSON Endpoint, it does everything correctly.
http://blogs.msdn.com/b/davrous/archive/2010/12/14/how-to-open-a-wcf-ria-services-application-to-other-type-of-clients-the-json-endpoint-4-5.aspx
http://channel9.msdn.com/Shows/SilverlightTV/Silverlight-TV-26-Exposing-SOAP-OData-and-JSON-Endpoints-for-RIA-Services

WebGet and non WebGet methods in WCF Rest Service

Following is my Contract and the OperationContracts, my issue is when I'm going with WebGet attribute to all the methods my service is working fine, when I remove WebGet Attribute to any one of the OperationContracts im getting following error.
Operation 'ProductDetails' of
contract 'IDemo' specifies multiple
request body parameters to be
serialized without any wrapper
elements. At most one body parameter
can be serialized without wrapper
elements. Either remove the extra body
parameters or set the BodyStyle
property on the
WebGetAttribute/WebInvokeAttribute to
Wrapped.
These are my methods
string AddNumbers(int x,int y); --- using [WebGet]
string SubtractNumbers(int x, int y); -- using [WebGet]
String ProductDetails(string sName, int cost, int Quntity, string binding); -- not using using [WebGet]
CompositeType GetDataUsingDataContract(CompositeType composite); -- not using [WebGet]
Is it mandatory to include [WebGet] attribute to all the operation contracts if we go for WebHttpbinding??.
public interface IService1
{
[OperationContract]
string GetData(int value,string binding);
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "/Add?num1={x}&num2={y}")]
string AddNumbers(int x,int y);
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "/Subtract?num1={x}&num2={y}")]
string SubtractNumbers(int x, int y);
[OperationContract]
String ProductDetails(string sName, int cost, int Quntity, string binding);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
The error message really says exactly what the problem is:
Operation 'ProductDetails' of contract
'IDemo' specifies multiple request
body parameters to be serialized
without any wrapper elements. At most
one body parameter can be serialized
without wrapper elements.
You cannot have methods which expect more than one parameter, unless you wrap those, e.g. by specifying the BodyStyle setting in the WebGet attribute.
So yes: either you have to apply a [WebGet] to each method of your REST service, or you can reorganize your methods to take in only a single parameter (e.g. by wrapping up the two or three parameters you have now into a single class that holds those multiple parameters, and then passing in an object instance of that Request class).
[DataContract]
public class AddNumbersRequest
{
[DataMember]
public int X { get; set; }
[DataMember]
public int Y { get; set; }
}
[OperationContract]
string AddNumbers(AddNumbersRequest request);