I have a C# application that takes and returns information via RESTful services. The way I handled these operations before, was the methods simply returned JSON strings, and these strings were then wrapped in XML.
To prevent this, I changed the ResponseFormat to JSON and I am now returning DataContracts, which all derive from one abstract class:
using System.Runtime.Serialization;
/// <summary>
/// Abstract class for defining a standard definition of a command return result.
/// A command is not required to return a result, however it is recommended.
/// A return result must implement the properties defined in this abstract class.
/// A command may return an anonymous object instead of an instance of this abstract class.
/// </summary>
[DataContract(Namespace = "")]
public abstract class ICommandResult {
// Don't let the name disturb you, this used to be an interface.
/// <summary>
/// Gets or sets the message.
/// </summary>
[DataMember]
public abstract string Message { get; set; }
}
To apply the DataContract attribute, I changed the above interface to a (now) abstract class.
Each derivative also applies the DataContract and DataMember attributes.
When I call a route of the service, the command gets executed, and I can see output in the console. When returning the return value, however, I see following in the console, and the web browser stays empty. Fiddler (on Windows) shows me a HTTP 504 error, whereas my Mac shows me a connection reset.
XmlException (Dropped Connection?): On JSON writer data type 'type' must be specified. Object string is 'object', server type string is '__type'.
The specific method I'm using to test this, is as follows:
IRestService:
[OperationContract]
[WebGet(UriTemplate = Routing.GetRoutesRoute, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
ICommandResult GetAllRoutes();
RestService (implementation):
public ICommandResult GetAllRoutes() {
var results = ExecuteCommand("print-routes", "--no-output");
// ExecuteCommand returns an ICommandResult object
return results;
}
Actual command implementation (so it can be called via the console, too):
ICommandResult Command_PrintRoutes(Command sender, params string[] args) {
var fields = typeof(Routing).GetFields();
var fieldValues = new Dictionary<string, string>();
var outputToConsole = true;
if (args.Count(x => x.ToLowerInvariant().Equals("--no-output")) == 1)
outputToConsole = false;
foreach (var field in fields)
fieldValues.Add(field.Name, (string)field.GetRawConstantValue());
var output = JsonConvert.SerializeObject(fieldValues, Formatting.Indented);
if (outputToConsole)
WriteLine(output);
//return new CommandResult<Dictionary<string, string>>("Found following routes", fieldValues);
return new CommandResult<string>("Found following routes (JSON-encoded)", output); // Trying out different return types
}
How can this be fixed and prevented?
I think that this is a serialization problem, with your abstact DataContract.
Look this: KnownTypeAttribute - Serialization.
I think that you need specify all your subclasses on the abstract base class using KnownType Attribute.
In this way the DataContractSerializer can recognize all types when serialize and deserialize objects.
[DataContract]
[KnownType(typeof(SubClassName1))] <-
[KnownType(typeof(SubClassName2))] <-
[KnownType(typeof(SubClassName3))] <-
public abstract class ClassName
{
...
}
Sorry for my english. I hope I was helpful,
Stefano