Haga que ASP.NET WCF convierta el diccionario a JSON, omitiendo las tags “Clave” y “Valor”

Aquí está mi dilema Estoy usando un servicio RESTful ASP.NET, tratando de obtener una función para devolver una cadena JSON en este formato:

{"Test1Key":"Test1Value","Test2Key":"Test2Value","Test3Key":"Test3Value"} 

Pero lo estoy obteniendo en este formato:

 [{"Key":"Test1Key","Value":"Test1Value"}, {"Key":"Test2Key","Value":"Test2Value"}, {"Key":"Test3Key","Value":"Test3Value"}] 

Mi método se ve así:

 [OperationContract] [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] public Dictionary Test(String Token) { if (!IsAuthorized(Token)) return null; if (!IsSecure(HttpContext.Current)) return null; Dictionary testresults = new Dictionary(); testresults.Add("Test1Key", "Test1Value"); testresults.Add("Test2Key", "Test2Value"); testresults.Add("Test3Key", "Test3Value"); return testresults; } 

¿Hay alguna forma de que pueda deshacerme de esas tags “Clave” y “Valor” usando solo herramientas ASP.NET integradas? (es decir, prefiero no usar JSON.NET, si es evitable)

¡Muchas gracias! 🙂

La clase de diccionario .NET no se serializará de ninguna otra manera que la forma descrita. Pero si crea su propia clase y ajusta la clase de diccionario, puede anular los métodos de serialización / deserialización y podrá hacer lo que desee. Vea el ejemplo a continuación y preste atención al método “GetObjectData”.

  [Serializable] public class AjaxDictionary : ISerializable { private Dictionary _Dictionary; public AjaxDictionary() { _Dictionary = new Dictionary(); } public AjaxDictionary( SerializationInfo info, StreamingContext context ) { _Dictionary = new Dictionary(); } public TValue this[TKey key] { get { return _Dictionary[key]; } set { _Dictionary[key] = value; } } public void Add(TKey key, TValue value) { _Dictionary.Add(key, value); } public void GetObjectData( SerializationInfo info, StreamingContext context ) { foreach( TKey key in _Dictionary.Keys ) info.AddValue( key.ToString(), _Dictionary[key] ); } } 

Me encontré con este problema hace unos meses y publiqué una pregunta algo menos que óptima aquí: Configuración del contrato de datos de WCF para una respuesta JSON adecuada

El problema que tuve entonces resultó ser el mismo que el de una pregunta mucho más precisa, en resumen: en el contexto de WCF, las herramientas de serialización asp.net estándar, para un diccionario, devolverán un ARRAY en lugar de una clave / valor par json OBJETO. Estoy publicando mi solución que funcionó para mí aunque recurrí al uso de JSON.NET (que me doy cuenta de que el póster estaba tratando de evitar). Sin embargo, tal vez esto sea útil para alguien.

 Function myDictionaryFunction () As Stream Implements IMywebservice.myDictionaryFunction Dim myKeyValuePairObject As Object = New Dynamic.ExpandoObject Dim myDictionary = DirectCast(myKeyValuePairObject, IDictionary(Of String, Object)) myDictionary.Add("Test1Key", "Test1Value") myDictionary.Add("Test2Key", "Test2Value") myDictionary.Add("Test3Key", "Test3Value") strJson = JsonConvert.SerializeObject(myKeyValuePairObject) Dim resultBytes As Byte() = Encoding.UTF8.GetBytes(strJson) WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain" Return New MemoryStream(resultBytes) End Function 

El resultado:

 {"Test1Key":"Test1Value","Test2Key":"Test2Value","Test3Key":"Test3Value"} 

El objeto expando funciona como un amuleto. Pero para que funcione, debes obligar a WCF a devolver el texto sin formato, lo que uno pensaría que es fácil pero no lo es. Debe implementar RawContentTypeMapper como se sugiere aquí: http://referencesource.microsoft.com/#System.ServiceModel.Web/System/ServiceModel/Channels/RawContentTypeMapper.cs … Y luego tiene que perder el tiempo con su web. archivo de configuración algo como esto:

        

Soy el primero en admitir que esta solución probablemente no recibirá ningún premio por elegancia. Pero funcionó y la devolución del contenido sin procesar de un servicio web de WCF proporcionará, si es necesario, un control adicional sobre cómo serializar su carga de datos de WCF. Desde que implementé esto, he migrado cada vez más a ASP.NET Web API (lo que hace que devolver algo RESTful sea mucho más fácil que WCF, IMO).

Ampliando ligeramente la excelente solución de @ MarkisT, puede modificar el constructor de serialización para recrear uno de estos diccionarios desde el mismo JSON (lo que le permite tomar un AjaxDictionary como parámetro de servicio), de la siguiente manera:

 public AjaxDictionary( SerializationInfo info, StreamingContext context ) { _Dictionary = new Dictionary(); foreach (SerializationEntry kvp in info) { _Dictionary.Add((TKey)Convert.ChangeType(kvp.Name, typeof(TKey)), (TValue)Convert.ChangeType(kvp.Value, typeof(TValue))); } } 

En caso de que alguien tenga ese problema en el lado del cliente: la conversión de esa extraña clave {Key: “x”, Value: “y”} Array a un objeto {x: “y”} se puede hacer en una sola línea de JS:

 var o = i.reduce(function (p, c, a, i) { p[c.Key] = c.Value; return p }, {}); 

siendo yo la matriz devuelta por el servicio, y o siendo lo que realmente deseas.

atentamente

evitando el “__type” en json …

en el webapi.config, hay varias opciones (mira el último):

  // To disable tracing in your application, please comment out or remove the following line of code // For more information, refer to: http://www.asp.net/web-api //config.EnableSystemDiagnosticsTracing(); // Use camel case for JSON data. config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // The setting will let json.net to save type name in the payload if the runtime type is different with the declare type. // When you post it back, json.net will deserialize the payload to the type you specified in the payload. // source: http://stackoverflow.com/questions/12858748/asp-net-webapi-posting-collection-of-subclasses //config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;