WCF 中默认使用的DataContractJsonSerializer
使用 XML 到 JSON 的映射,这会导致一些问题,例如您在ISerializable
类型中看到的问题。但是,您可以使用自定义格式化程序来更改响应的序列化方式。在下面的示例中,我使用的是ISerializable
“正确”处理对象的 JSON.NET。
public class StackOverflow_16674152
{
[Serializable]
public class JsonDictionary : ISerializable
{
private Dictionary<string, object> _Dictionary;
public JsonDictionary()
{
_Dictionary = new Dictionary<string, object>();
}
public JsonDictionary(SerializationInfo info, StreamingContext context)
{
_Dictionary = new Dictionary<string, object>();
SerializationInfoEnumerator enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
_Dictionary.Add(enumerator.Name, enumerator.Value);
}
}
public object this[string key]
{
get { return _Dictionary[key]; }
set { _Dictionary[key] = value; }
}
public void Add(string key, object value)
{
_Dictionary.Add(key, value);
}
public bool ContainsKey(string key)
{
return _Dictionary.ContainsKey(key);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in _Dictionary.Keys)
info.AddValue(key, _Dictionary[key], _Dictionary[key] == null ? typeof(object) : _Dictionary[key].GetType());
}
}
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
[MyISerializableResponseJsonBehavior]
public JsonDictionary GetCollection()
{
JsonDictionary dict = new JsonDictionary();
dict.Add("Hello world", 100);
return dict;
}
}
public class MyFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter original;
string replyAction;
public MyFormatter(IDispatchMessageFormatter original, string replyAction)
{
this.original = original;
this.replyAction = replyAction;
}
public void DeserializeRequest(Message message, object[] parameters)
{
this.original.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
ISerializable serializable = result as ISerializable;
if (serializable != null)
{
string json = JsonConvert.SerializeObject(serializable);
byte[] bytes = Encoding.UTF8.GetBytes(json);
var writer = new MyRawWriter(bytes);
Message reply = Message.CreateMessage(messageVersion, replyAction, writer);
reply.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return reply;
}
else
{
return this.original.SerializeReply(messageVersion, parameters, result);
}
}
class MyRawWriter : BodyWriter
{
byte[] data;
public MyRawWriter(byte[] data)
: base(true)
{
this.data = data;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Binary");
writer.WriteBase64(data, 0, data.Length);
writer.WriteEndElement();
}
}
}
public class MyISerializableResponseJsonBehaviorAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
if (operationDescription.Messages.Count > 1)
{
dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter, operationDescription.Messages[1].Action);
}
}
public void Validate(OperationDescription operationDescription)
{
if (operationDescription.Messages.Count > 1)
{
var respMessage = operationDescription.Messages[1];
if (respMessage.Body.Parts.Count > 0)
{
throw new InvalidOperationException("Cannot be used with out/ref parameters");
}
}
var wga = operationDescription.Behaviors.Find<WebGetAttribute>();
var wia = operationDescription.Behaviors.Find<WebInvokeAttribute>();
WebMessageBodyStyle bodyStyle = WebMessageBodyStyle.Bare; // default
if (wga != null && wga.IsBodyStyleSetExplicitly) {
bodyStyle = wga.BodyStyle;
}
if (wia != null && wia.IsBodyStyleSetExplicitly) {
bodyStyle = wia.BodyStyle;
}
if (bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedResponse)
{
throw new InvalidOperationException("This behavior can only be used with bare response style");
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/"));
}
}