2013-05-21 2 views
3

Я пытаюсь вернуть словарь из службы REST WCF как объект JSON. Я хочу, чтобы формат был {"ключ": "значение"} , поэтому я создал свой собственный класс, как описано here и here.JSON ответ от WCF escapes значения имени

Метод обслуживания работает, вроде. Проблема в том, что имена ключей экранированы. Например, если мой словарь содержит «Hello world»: 100, я получаю {«Hello_x0020_world»: 100}. Он также избегает других символов, таких как% и т. Д.

Есть ли какой-то способ, которым я могу сказать, что сериализация не позволяет избежать имен таким образом? Это почти похоже на использование правил xml, которые (необязательно) не применяются к JSON.

Мой сериализации класс:

[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; 
    } 
+0

Мне всегда нравилось, как JSON работает в браузере. Если вы не справитесь с накладными расходами, вам понадобится компрессор JSON в потоке и декомпрессор на клиенте. Есть много там – danny117

ответ

1

DataContractJsonSerializer используется по умолчанию в WCF использует отображение XML-в-JSON, который вызывает некоторые проблемы, как один вы видите в ISerializable типах. Тем не менее, вы можете использовать собственный форматтер, чтобы изменить способ генерации ответа. В приведенном ниже примере я использую JSON.NET, который имеет дело с ISerializable объектами «правильно».

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 + "/")); 
    } 
} 
+0

Этот ответ решил проблему для меня. Он должен быть отмечен как ответ. – AntonioJunior

Смежные вопросы