3

我正在尝试将 WCF REST 服务中的字典作为 JSON 对象返回。我希望格式为 {"key": "value"} 所以我创建了自己的类,如此此处所述。

服务方法有效,有点。问题是键名被转义了。例如,如果我的字典包含“Hello world”:100,我会得到 {“Hello_x0020_world”:100}。它还可以转义其他字符,例如 % 等。

有什么方法可以告诉序列化不要以这种方式转义名称吗?几乎就像它使用的是不(不一定)适用于 JSON 的 xml 规则。

我的可序列化类:

[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());  
    }
}

我的服务功能定义:

[WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
    public JsonDictionary GetCollection()
    {
        JsonDictionary dict = new JsonDictionary();

        dict.Add("Hello world", 100);
        return dict;
    }
4

1 回答 1

1

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 + "/"));
    }
}
于 2013-11-26T21:14:03.323 回答