How can I return json from my WCF rest service (.NET 4), using Json.Net, without it being a string, wrapped in quotes? - wcf

UPDATE 10/19/2010
I know I asked this question a while ago, but the workarounds shown in these answers are hardly satisfactory, and this is still a common problem for many. WCF just isn't flexible. I started my own open source C# library for creating REST services without WCF. Check restcake.net or rest.codeplex.com for info on said library.
END UPDATE
UPDATE 8/2/2012
ASP.NET Web API (previously WCF Web API, the replacement for REST WCF) uses Json.NET by default
END UPDATE
The DataContractJsonSerializer is unable to handle many scenarios that Json.Net handles just fine when properly configured (specifically, cycles).
A service method can either return a specific object type (in this case a DTO), in which case the DataContractJsonSerializer will be used, or I can have the method return a string, and do the serialization myself with Json.Net. The problem is that when I return a json string as opposed to an object, the json that is sent to the client is wrapped in quotes.
Using DataContractJsonSerializer, returning a specific object type, the response is:
{"Message":"Hello World"}
Using Json.Net to return a json string, the response is:
"{\"Message\":\"Hello World\"}"
I do not want to have to eval() or JSON.parse() the result on the client, which is what I would have to do if the json comes back as a string, wrapped in quotes. I realize that the behavior is correct; it's just not what I want/need. I need the raw json; the behavior when the service method's return type is an object, not a string.
So, how can I have my method return an object type, but not use the DataContractJsonSerializer? How can I tell it to use the Json.Net serializer instead?
Or, is there someway to directly write to the response stream? So I can just return the raw json myself? Without the wrapping quotes?
Here is my contrived example, for reference:
[DataContract]
public class SimpleMessage
{
[DataMember]
public string Message { get; set; }
}
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PersonService
{
// uses DataContractJsonSerializer
// returns {"Message":"Hello World"}
[WebGet(UriTemplate = "helloObject")]
public SimpleMessage SayHelloObject()
{
return new SimpleMessage("Hello World");
}
// uses Json.Net serialization, to return a json string
// returns "{\"Message\":\"Hello World\"}"
[WebGet(UriTemplate = "helloString")]
public string SayHelloString()
{
SimpleMessage message = new SimpleMessage() { Message = "Hello World" };
string json = JsonConvert.Serialize(message);
return json;
}
// I need a mix of the two. Return an object type, but use the Json.Net serializer.
}

I finally figured out a solution to this. It's not what I would have preferred (which would be to return the specific object type, and somehow instruct WCF to use a Json.Net serializer, instead of the DataContractJsonSerializer), but it is working great, and it's simple and clear.
Extending my contrived example using this new solution:
[WebGet(UriTemplate = "hello")]
public void SayHello()
{
SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
string json = JsonConvert.Serialize(message);
HttpContext.Current.Response.ContentType = "application/json; charset=utf-8";
HttpContext.Current.Response.Write(json);
}
Note the return type of void. We do not return anything, since it would be serialized with DataContractJsonSerializer. Instead, I write directly to the response output stream. Since the return type is void, the processing pipeline doesn't set the content-type to the default type of "application/json", so I set it explicitly.
Because this uses HttpContext, I'm guessing it will only work if you have [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] on your service class, since that will force requests to the service to go through the ASP.NET pipeline. Without the asp.net compatibility, the HttpContext will not be available, since wcf hosting is supposed to be host agnostic.
Using this method, the results look perfect in firebug for GET requests. Correct content-type, correct content length, and raw json, not wrapped in quotes. And, I'm getting the serialization I want using Json.Net. Best of both worlds.
I'm not 100% positive of what obstacles I might run into regarding deserialization, when my service methods have [DataContract] object types as input parameters. I'm assuming the DataContractJsonSerializer will be used for that too. Will cross that bridge when I come to it...if it creates a problem. It hasn't so far, with my simple DTOs.
UPDATE
See Oleg's answer (the UPDATE2 part). He changes the return type of the service method from void to System.ServiceModel.Channels.Message, and rather than using HttpContext.Current.Response.Write(), he uses:
return WebOperationContext.Current.CreateTextResponse (json,
"application/json; charset=utf-8", Encoding.UTF8);
Which is indeed a better solution. Thank you Oleg.
UPDATE 2
There is yet another way of accomplishing this. Change your service's return type from Message to Stream, and return this:
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));
I haven't done any specific tests, but it's possible that this would be a better choice for methods that could potentially return large amounts of data. I don't know if that matters for non-binary data though. Anyway, a thought.

It seems to me that you use not correct DataContractJsonSerializer. What is strange is: you don't define ResponseFormat = ResponseFormat.Json attribute for the public SimpleMessage SayHelloObject() method.
Moreover if you have {"Message":"Hello World"} in a string and display it in debugger it will be display as "{\"Message\":\"Hello World\"}", so exactly like you see string json = JsonConvert.Serialize(message); (Json.Net). So it seems to me that you have in both cases the same results.
To verify this use a client software which read the results. See some examples
JQuery ajax call to httpget webmethod (c#) not working
Can I return JSON from an .asmx Web Service if the ContentType is not JSON?
How do I build a JSON object to send to an AJAX WebService?
UPDATED: In your code you define method SayHelloString(). It's result are a string. If you call the method this string will be one more time JSON serialized. JSON serialization of the string {"Message":"Hello World"} is a quoted string (see http://www.json.org/ definition for not a object, but a string) or exactly string "{\"Message\":\"Hello World\"}". So everything is correct with both methods of your Web Service.
UPDATED 2: I am glad that my tip from "Update" part of my answer helped you to swich of the double JSON serialization.
Nevertheless I would recommend you to change a little the solution to stay more at the WCF concept.
If you want implement a custom encoding of the web responce in WCF (see http://msdn.microsoft.com/en-us/library/ms734675.aspx) your WCF method should better return Message instead of void:
[WebGet(UriTemplate = "hello")]
public Message SayHello()
{
SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
string myResponseBody = JsonConvert.Serialize(message);
return WebOperationContext.Current.CreateTextResponse (myResponseBody,
"application/json; charset=utf-8",
Encoding.UTF8);
}
You can of cause use another Message formater: for example CreateStreamResponse (or some other see http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx) instead of CreateTextResponse.
If you want to set some additional HTTP headers or Http status code (for example in case of some error) you can do this with this way:
OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
ctx.StatusCode = HttpStatusCode.BadRequest;
At the end I want repeat my question from a comment: could you explain why you want use Json.Net instead of DataContractJsonSerializer? Is it performance improvement? Do you need implement serialization of some data types like DateTime in other way as DataContractJsonSerializer do? Or the main reason of your choose of Json.Net is some other?

Related

WCF Custom Client Inspector

An external company has given me a WSDL to consume which has a couple of odd characteristics which I don't want to impact my client code.
Firstly, each OperationContract requires the same username parameter sent over. Instead of setting this each time in my client code I'd like to do this globally.
I believe setting this in a IClientMessageInspector is my best bet, however, with this being a SOAP service I'm a little confused at how to add this into the body.
public class CustomInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Add an additional parameter to the SOAP body
return null;
}
}
Secondly, whilst the service does return mapped objects, one of the objects contains an xml document shoved in a CDATA :(
<a:ResponseData>
<![CDATA[ INSERT XML DOCUMENT HERE]]>
</a:ResponseData>
I'm looking to extract the XML out and add it back in without the CDATA and XML declaration so I can add the appropriate properties on my response object. That way it should deserialize like normal(hope that makes sense)
public class CustomInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Get the XML from the ResponseData element and remove the CDATA. Add the XML back in (Minus the <xml> declaration)
}
}
Firstly, each OperationContract requires the same username parameter
sent over. Instead of setting this each time in my client code I'd
like to do this globally. I believe setting this in a
IClientMessageInspector is my best bet, however, with this being a
SOAP service I'm a little confused at how to add this into the body.
If you want to add custom message header to the message, you could refer to the following code.
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("username", "", "user"));
request.Headers.Add(MessageHeader.CreateHeader("password", "", "pass"));
return null;
}
Take a look at IClientMessageInspector.
Here are some links may be useful to you.
Adding custom SOAP headers from Silverlight client
https://weblogs.asp.net/paolopia/handling-custom-soap-headers-via-wcf-behaviors
https://social.msdn.microsoft.com/Forums/vstudio/en-US/f1f29779-0121-4499-a2bc-63ffe8025b21/wcf-security-soap-header

WCF Polymorphism in service contract

I am trying to create a service that with an operation that accepts, as a parameter, an any object that implements a specific interface. I would have thought this would be easy to accomplish, but I am running into problems (what I am guessing to be serialization problems but I am not certain). I have the following contract:
//Unsustainable because I would need a method for each of the (currently)
//3 student types, plus I have 2 more root categories that have multiple subtypes
public interface IEmailTemplateAccess
{
[FaultContract(typeof(ValidationFault))]
[FaultContract(typeof(ErrorResponse))]
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
TemplateResponse GetStudentTemplate(ITemplateRequest request);
}
And this is what I would like it to look like:
public interface IEmailTemplateAccess
{
[FaultContract(typeof(ValidationFault))]
[FaultContract(typeof(ErrorResponse))]
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
TemplateResponse GetTemplate(ITemplateRequest request);
}
In my service I use an abstract factory to return the correct template based on the type of request that comes in. In addition, I have created concrete ITemplateRequests for the different kinds of templates that could be returned. For example, I have Template Request types A and B. Template Request Type A can have one of 3 sub types, SubType1, SubType2 and SubType3. I then created a SubType3 request that implemented the ITemplateRequest interface (SubType3Request).
I would hate to have to create a method for each request type I have (i.e. GetSubType1Template, GetSubType2Template, GetSubType3Template, GetTypeBTemplate, etc) as this would quickly become unwieldy as the types of templates I can get will be changing occasionally.
Is there a way to have a contract method accept anything that implements ITemplateRequest as a parameter and let my factory do the work of figuring out what type of template to get?
So far, I have the following methods in my service:
//Not a part of the contract right now although I would like it to be
public IEmailTemplate GetTemplate(ITemplateRequest request)
{
TemplateFactoryBuilder builder = new TemplateFactoryBuilder();
ITemplateFactory factory = builder.GetTemplateFactory(request.Type);
var template = factory.GetTemplate(request);
return template;
}
//contract method --This would be my Parent Request Type (RequestTypeA) from above.
//There are 3 subtypes of the Student type
public TemplateResponse GetStudentTemplate(StudentEmailTemplateRequest request)
{
var response = new TemplateResponse
{
RequiresProcessing = true
};
response.Template = (EmailMergeTemplate) GetTemplate(request);
return response;
}
Sorry for the link-ish answer, but it's pretty long.. What you're after (I think) is here: http://blogs.msdn.com/b/morgan/archive/2009/08/05/polymorphism-in-wcf.aspx
It boils down to using known types. Something like this;
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(CommandServiceHelper))]
public interface ICommandService

WCF deserialization of type object Properties

I am having trouble achieving the following scenario.
We currently have a method which expects a list of 'context' key value pairs. The value however can be of any type.
the goal is to make this method available using WCF. So I created a
public List<Element> Transform(List<Element> elements)
{
... Transformation of elements takes place
}
[DataContract(Namespace = Constants.NAMESPACE)]
public struct Element
{
[DataMember(Order = 0, IsRequired = true)]
public string Key;
[DataMember(Order = 1, IsRequired = true)]
public object Value;
}
When I use a .Net test project everything works fine.
However, when I call this service using SOAP UI I get an error message:
The formatter threw an exception while trying to deserialize the
message: There was an error while trying to deserialize parameter
elements. The InnerException message was 'Element Value cannot have
child contents to be deserialized as an object. Please use XmlNode[]
to deserialize this pattern of XML.'.
I am having trouble figuring out what to do. any help appreciated.
the xml i use is this:
<ws:Transform>
<ws:elements>
<ws:Element>
<ws:Key>Key1</ws:Key>
<ws:Value>A</ws:Value>
</ws:Element>
<ws:Element>
<ws:Key>Key2</ws:Key>
<ws:Value>B</ws:Value>
</ws:Element>
<ws:ScriptName>SetVariable</ws:ScriptName>
</ws:elements>
</ws:Transform>
In this case, SoapUI uses .Net technology which does not understand target type for object.
sending object is not valid across all platforms. In fact you might get an error with a .Net client as well. Your best bet is create a generic xml representation of the Value and have all clients inflate the object from the xml

Why am I getting an InvalidOperationException after adding a [MessageContract] attribute?

The operation 'PRPA_IN201301UV02' could not be loaded because it has a parameter or return type of type System.ServiceModel.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using System.ServiceModel.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters.
I'm running a WCF on a console host, this is the contract:
[MessageContract]
public class opRequest
{
[MessageBodyMember]
public string myProperty;
}
[ServiceContract(Namespace = "urn:hl7-org:v3")]
public interface IHL7v3
{
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02")]
string PIXManager_PRPA_IN201301UV02(opRequest clientID);
}
It does run when I remove from opRequest class the [MessageContract] and [MessageBodyMember]
I'm completely not sure if that will get me to what I need, so i'll give the wider scope - I'm trying to get the SOAP body to be without an enclosing tag of the parameter name.
for example (the body extract from the SOAP message) instead of:
<s:Body>
<PRPA_IN201301UV02 xmlns="urn:hl7-org:v3">
<clientID>the xml document is enclosed</clientID>
</PRPA_IN201301UV02>
I want it to be like this:
<s:Body>
<PRPA_IN201301UV02 xmlns="urn:hl7-org:v3">
my given xml document will go here...
</PRPA_IN201301UV02>
I need it like that to conform to a standard (HL7v3 PIX Manager SOAP Web Service).
Any ideas?
Looks like you should use MessageContract for your return parameter as well
EDITED:
Have a look at this MSDN article for more details Using Message Contracts
If you design your contract with messages you can't use other types either as a parameter or a return value.
Here is a code snippet from the article:
[OperationContract]
bool Validate(BankingTransaction bt);
// Invalid, the return type is not a message contract.
[OperationContract]
void Reconcile(BankingTransaction bt1, BankingTransaction bt2);
// Invalid, there is more than one parameter.
Did you try to use an XMLDocument as the input parameter instead of opRequest? You will also have to mark the interface to use the XML Serializer:
[ServiceContract(Namespace = "urn:hl7-org:v3")]
[XmlSerializerFormat]
public interface IHL7v3
{
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02")]
XMLDocument PIXManager_PRPA_IN201301UV02(XMLDocument doc);
}
I am presuming you are also returning XML.
Please note that this is wide open - any XML can be sent which may not be what you intend as there no explicit data contract.

How to serialize Dictionary<string, string> through WCF?

I have a data contract with a data member typed as Dictionary<string, string>.
The generated web service reference exposes this as a member with the type ArrayOfKeyValueOfstringstringKeyValueOfstringstring[].
Has anyone seen this before?
WCF only serializes structure, because what ends up on the wire must be self-contained enough to be useful for any client, not just .NET clients.
Some client developed on a different platform may not share the concept of a 'dictionary', so it would be too constraining to serialize a Dictionary to a representation that carries an implicit knowledge about the underlying class.
The client may not even be object-oriented.
A Dictionary is more than structure - it also contains behavior (e.g. when you assign to a key that already exists, you overwrite that key, etc.) and that behavior cannot travel across the wire.
In other words, Dictionaries and many other .NET types are not interoperable, so WCF will not attempt to preserve them in a ServiceContract.
You would probably be better off designing a custom DataContract for your data.
As WCF has to convert everything to XML, it has to fit as XML... Collections are generally converted to arrays.
A Dictionary is prety hard to represent as xml, that's why you have this type on the other side. You can specify SvcUtil.exe to use specific collections instead of arrays in the generated proxy code, but I'm not sure it will work for a Dictionary. I would say that you should avoid using a Dictionary here, and use a simpler Collection.
What I would do is create my own data type, [DataContract] it, make it have two fields of type String, then make a Collection of these that you fill with everthing you find in the Dictionary. Then sent that Collection trough the wire, then convert it back to a Dictionary on the other side.
There is a way to do it.. The operation contract is actually a string. I escape the json string to keep it a string. I then in the web method unescape the string and parse it into a dictionary using NewtonSoftJson I used Dictionary but you could also do Dictionary if you wanted. Hope this is useful...
I used NewtonSoft Json library...
c# code
using Newtonsoft.Json;
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json)]
public void testMethod(string jsonData)
{
string data = Uri.UnescapeDataString(jsonData);
Dictionary<string, string> x = jsonConvert.DeserializeObject<Dictionary<string, string>>(data);
foreach (KeyValuePair<string, string> kvp in x)
{
}
}
JSCode
var Data = {
width: 400,
height: 200,
someString: "somedata"
};
$.ajax({
type: "POST",
url: "Service1.svc/testMethod",
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout: 1000000,
data: '{"jsonData": "' + escape(JSON.stringify(Data)) + '"}',
error: function(error) {
},
success: function (data) {
},
});