Since updating our project to Visual Studio 2012 and a targeting the .NET framework 4.5 from 4.0, we've been intermittently experiencing serialization exception issues when attempting to return data from our service methods, though for purposes of debugging the problem, we've focused our attention on a single service method, GetPageSelectListForSite.
This methods interface declaration is the following:
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/Pages/Site/ListItems/{siteId}",
BodyStyle = WebMessageBodyStyle.Bare),
Description("")]
IEnumerable<PageInfoItem> GetPreviousPages(string siteId);
It is called with the same signature parameter, and the same IEnumerable data is returned inside of a controller; however, occassionally (once every 100 or so times) this will result in a serialization exception when the controller is trying to deserialize the REST service reply:
System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type System.Collections.Generic.IEnumerable`1[[Synodine.Hkd.Models.PageInfoItem, Synodine.Hkd.Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. Encountered unexpected character 'D'. ---> System.Xml.XmlException: Encountered unexpected character 'D'.
The full exception and corresponding stack trace is available here:
http://pastebin.com/jaqQDbru
PageInfoItem is a fairly simple class containing a string, and derives from ListInfoBase which contains a guid, some strings, some bools, and a DateTime.
Attempts to decorate the classes with [Serializable] AND/OR [DataContract]/[DataMember] have not had any effect. We have not been able to consistently reproduce the problem.
I should note that we've seen the serialization exception error occur on an extremely simple REST GET method, with response/request formats set to Json and BodyStyle of Bare that returns a single string. It doesn't seem to matter if the rest service method in question is a POST/GET or whether its style is set to Bare or Wrapped, though in all cases we are making use of the WebMessageFormat.Json.
We've tried serializing the data ourselves and logging it to see if the JSON was malformed in some way, but the JSON is valid and is identical across failed/succeeded requests.
This was the code we used to serialize the data:
var stream1 = new MemoryStream();
var ser = new DataContractJsonSerializer(typeof(IEnumerable<PageInfoItem>));
ser.WriteObject(stream1, PageInfoItems);
stream1.Position = 0;
var streamReader = new StreamReader(stream1);
A sample of the JSON that is being logged is:
[
{
"DateLastUpdate": "\/Date(1362438368000+0000)\/",
"DateLastUpdateString": null,
"Id": "df2544e6-71a3-4f1c-ac54-d3c85269804f",
"IsSelected": false,
"Name": "samplepage",
"Path": "\/samplepage"
},
<snip>
]
This problem only occurs in our deployed environments. All attempts to reproduce the problem on our local IIS webservers have failed.
We are using the WebChannelFactory to consume the WCF REST Services. Because of this, we don't have (and are not sure how to get) direct access to the Response so we cannot be 100% of the data being sent across the wire.
Has anyone encountered anything similar to this when upgrading their projects to Visual Studio 2012/.NET 4.5 Framework?
Any responses definitely appreciated,
-- Shaun
Can you try exposing another function which return same data that GetPreviousPage returns, but changing the ResponseFormat to say Xml? Then call it multiple times (e.g. from a browser) and see if this new function ever fails. This well help you know if the problem is with Json Serializer.
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Xml,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "/Pages/Site/ListItemsNew/{siteId}",
BodyStyle = WebMessageBodyStyle.Bare),
Description("")]
IEnumerable<PageInfoItem> GetPreviousPagesNew(string siteId);
PS: Posted this as an answer as it's difficult to post code in comment.
Related
I use multiple API coded in c# that works well. I want to use one receiving an anonymous object (I don't want to create a Class). I have a problem when I try to deserialize the object.
I have an API following this scheme, it works well when it's called from python using the json_dumps function. But when I try with JSON.stringify (from an a or even POSTMAN, I have a 400 bad request.
Here is my code, I have tried a lot of things :
[WebInvoke(Method = "POST", UriTemplate = "myUrl")]
[OperationContract]
public Message myMethod(object objectSentByUser)
{
var perso = JsonConvert.DeserializeObject<dynamic>(objectSentByUser.ToString());
JsonConvert.DeserializeObject<dynamic> waits for a string, I tried:
-to specify objectSentByUser as a string in the argument of myMethod
When I do so, I've got a 400 without even entering the method (I tried to send a JSON, to add quotes, to send a string etc...)
-to cast with (string)objectSentByUser, it doesn't work
-to use the toString() method, which leads to the next error:
Unexpected character encountered while parsing value: S. Path '', line 0, position 0
which is quite normal because objectSentByUser.toString() returns "System.Object"
(but why does it work when used with python json_dump?)
This code works when called with python function json_dump that returns an object like this:
"{\\"key1\\":\\"value1\\",...}"
From Postman I send a classic POST with application/json as contentType and a valid JSON in the body (verified on a website found in an another discussion on stackoverflow)
Thanks a lot for your help
See you
If the user sends a valid json string to your action, then don't accept an object as a parameter, but rather a string (i.e. because your user sends you one).
If you call ToString() on an object, chances are, it's not in a Json format.
Try accepting a string and deserializing that:
[WebInvoke(Method = "POST", UriTemplate = "myUrl")]
[OperationContract]
public Message myMethod(string jsonSentByUser)
{
var perso = JsonConvert.DeserializeObject<dynamic>(jsonSentByUser);
We have a WCF Rest Json service hosted on the server and it consume from the native iPad application. Most of the users , it works fine while calling WCF service. But for some users, we found that WCF receives the currupted or clipped Json string. Some part of the Json string is clipped and it failed to deserialize from the service end.
For the account creation issue , we have added the logging of the service exception in the webservice to the notepad file. We got the exceptions from the notepad file. Right now we could see the parameters to the services is missing for those users.and it cause the error while deserializing the parameter. We need to check the app side why the the parameters are coming as empty. Right now we got the same user account creation exceptionentries from the "50.143.85.75" IP.
We have WCF Rest service CreateUser"as like below. We are consuming this service from the IOS app.
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json,
UriTemplate = "CreateUser/")]
[OperationContract]
CustomServiceResponse CreateUser(Stream userdetails);
Method Implementation code is like below.
public CustomServiceResponse CreateUser(Stream user)
{
var stream = new StreamReader(user);
string streamcontent = stream.ReadToEnd();
UserInformation userInformation = ConvertJSonToObject<UserInformation>(streamcontent);
}
Following are the biding configuration
<binding name="webHttpBindingStreamed"
transferMode="Streamed"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647"
openTimeout="00:25:00"
closeTimeout="00:25:00"
sendTimeout="00:25:00"
receiveTimeout="00:25:00"></binding>
</webHttpBinding>
Right now the service converting the stream to string and doing all operations. But for sometime we are getting error.
We have logged the streamcontent when the deserialization failure. we could see that serialization failing when the streamcontent is not containg full json string. below you can see the json string we got while we got the exception. you can see the double quotes and closing bracket missing from the first json string and some letter ("false") and closing bracket skipped from second json String.
{"FirstName":"Donald","LastCBPaymentType":6,"AcceptEmail":true,"CellPhone":"111-111-6874","Position":"3","UserAccountDateTime":"\/Date(1457055496621+0800)\/","CBExpirationDate":"\/Date(1465862400000+0800)\/","Email":"test.test#gmail.com","Password":"test","IsNeedtoGiveFreeOneMonth":false,"LastName":"test","WBExpirationDate":null,"IpAddress":"72.188.215.34","CarrierNum":"4","EmpNum":"96874
{"Position":"4","Email":"Tim.rrrr#wnco.com","CellPhone":"111-11-6146","LastCBPaymentType":6,"EmpNum":"107333","FirstName":"Timothy","Password":"eeee","WBExpirationDate":null,"UserAccountDateTime":"\/Date(1457054214674+0800)\/","CarrierNum":"5","CBExpirationDate":"\/Date(1468281600000+0800)\/","AcceptEmail":true,"IpAddress":"73.209.67.242","LastName":"Hill","IsNeedtoGiveFreeOneMonth":fa
Do you know why this some portion of the json string clipped ? Is there any serialization issue from the IOS side?
I have one WCF service and one single method named GetStudentList() in the service.It's working fine when it's return single response.Something like this
[WebGet(ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
List<Student> GetStudentList();
But i want to return multiple response i.e. xml and json both.something like this
[WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[WebGet(ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
List<Student> GetStudentList();
Is it possible?if yes then how?
It is possible in .NET 4.0 but not in the way you specified it. .NET 4.0 adds new parameter to WebHttp behavior:
<endpointBehaviors>
<behavior name="WebHttp">
<webHttp automaticFormatSelectionEnabled="true" />
</behavior>
</endpointBehaviors>
When you use automatic format selection the format of the response is based on:
Accept header from the request
Content type of the request
Default format specified on operation
Default format specified in webHttp behavior
So if you call your REST service with JSON request you will get JSON. If you call it with POX request you will get XML. Full description of automatic format selection is in MSDN.
I do not think it is possible to return the object as both Json and XML using one call. Think of WCF like a normal method call in this regard; you call one method, you get one serialized return value. Once the service has returned one response to the caller, the call is complete.
Think carefully about why you want to use both response types; they are both informative, universal standards for object serialization, and using WCF, you would only need both if you were using the serialized response text directly. If at all possible, I would refactor the clients to work with the same response type.
The simplest workaround, if two types really are needed, would be to provide two "overloads" of this method, and make each client type smart enough to know which call it needs to make. Because the difference is not in the method signature, it's not a true overload; you'll have to separate them either by name (GetStudentListJSON vs GetStudentListXML) or by locating the methods in different service classes.
You could also always return one response type, and convert on the client side by deserializing/reserializing when you need the object serialized in the other format. This does require you to be using .NET code that you have control over on the client side of the call.
I don't know of a way where you can get 2 outputs from a service operation. You can always get the XML (serialized DataContract) and then "JSON Serialize" it. Here is how you can do it.
List<Student> studentList = GetStudent();
string jsonString = JsonSerialize(studentList.GetType(), studentList);
And then add this function to a utility class:
public static string JsonSerialize(Type type, object objectGraph)
{
MemoryStream memoryStream = new MemoryStream();
try
{
System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(type);
serializer.WriteObject(memoryStream, objectGraph);
return Encoding.Default.GetString(memoryStream.ToArray());
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (memoryStream != null) memoryStream.Close();
}
}
Is there a way to dynamically change the WebResponseFormat on a method given a parameter passed by the client? I default my WebResponseFormat to XML, but I want to give the client the opportunity to specify a format as JSON or XML and if none is specified, default to XML.
Currently I am doing the following:
[WebGet(UriTemplate = "objects", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
List<SampleObject> GetObjects();
The user can call it via:
http://localhost/rest/myservice/objects
They then can specify a format by doing:
http://localhost/rest/myservice/objects?format=json
The problem is that when I try to set the response content type via:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
That just returns the XML but the browser attempts to process it like a JSON object instead of serializing the response as JSON.
Is this even possible with .NET 3.5 outside of using a Stream as the return value and serializing the response myself? If not, is there a better solution?
I was able to resolve this by doing the following:
[WebGet(UriTemplate = "objects", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract]
List<SampleObject> GetObjects();
[WebGet(UriTemplate = "objects?format=json", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
List<SampleObject> GetObjectsInJson();
It isn't pretty, but if format=xml is defined or left off, it will default to my operation contract, but if format=json is defined, it defaults to the second operation contract. This successfully returns the result as JSON and XML as desired.
For .NET 3.5 and WCF REST 3.5, I don't know of any way to do this elegantly.
.NET 4 and WCF REST in .NET 4 will support the "content negotiation" strategy that you use - just setting the ContentType = "application/json" will cause the service to automagically return JSON instead of XML.
So if there's any chance for you, wait for .NET 4 (should be out mid April 2010) and use that - it should offer lots of improvements in WCF anyway, especially in the WCF REST area.
i am using the Plain old XML template from WCF REST Starter Kit Preview 2 to build a REST service and i would like to set the ResponseFormat inside the OperationContract, not outside(not with ResponseFormat = WebMessageFormat.Json) because i want to set it accordingly to the Accept Header in the Request. and ideally i would like to be able to set the RequestFormat from inside the OperationContract also, acording to the Content-Type header.
So, could anyone tell me how i can do it, or point me towards some examples.
chers,
Later Edit: so i don't want to have things like "format" in the UriTemplate, i just want one UriTemplate, no ResponseFormat (and no RequestFormat at some point) and the ResponseBody to be output according to the AcceptHeader
[WebHelp(Comment = "For Service testing purposes")]
[WebGet(UriTemplate = "Echo")]
[OperationContract]
public ResponseBody Echo()
You'll have to strip off most of the WCF REST code and rebuild it yourself. There is no support for content-type negotiation out of the box. I am lead to believe the starter kit has an example using their extensibility points, you may want to check with them.
http://damianblog.com/2008/10/31/wcf-rest-dynamic-response/ is a good place to start