39 votos

¿Cómo puedo devolver json desde mi WCF resto de servicios (.NET 4), el uso de Json.Net, sin que sea una cadena, envuelto en una cita?

ACTUALIZACIÓN 10/19/2010 Sé que hice esta pregunta hace un tiempo, pero las soluciones que se muestran en estas respuestas son poco satisfactorios, y esto es todavía un problema común para muchos. WCF simplemente no es flexible. Yo empecé mi propio código abierto C# biblioteca para la creación de los servicios de DESCANSO sin WCF. Verificación restcake.net o rest.codeplex.com para más información sobre dicha biblioteca. ACTUALIZACIÓN DE FIN DE

ACTUALIZACIÓN 8/2/2012 ASP.NET la API de Web (anteriormente WCF, Web API, el reemplazo para el RESTO de WCF) utiliza Json.NET por defecto ACTUALIZACIÓN DE FIN DE

El DataContractJsonSerializer no es capaz de manejar muchas situaciones que Json.Net maneja muy bien cuando está correctamente configurado (en concreto, de los ciclos).

Un método puede devolver un tipo de objeto específico (en este caso un DTO), en cuyo caso el DataContractJsonSerializer , o puedo tener el método retorna una cadena, y realizar la serialización a mí mismo con Json.Net. El problema es que cuando me devuelven una cadena json como opuesto a un objeto json que se envía al cliente está envuelto en las comillas.

El uso de DataContractJsonSerializer, la devolución de un tipo de objeto específico, la respuesta es:
{"Message":"Hello World"}

El uso de Json.Net para devolver una cadena json, la respuesta es:
"{\"Message\":\"Hello World\"}"

No quiero tener a eval() o JSON.parse() el resultado en el cliente, que es lo que tendría que hacer si el json que se vuelve como una cadena, envuelto en las comillas. Me doy cuenta de que el comportamiento es correcto; simplemente no lo quiero/necesito. Necesito el raw json; el comportamiento cuando el método de servicio del tipo devuelto es un objeto, no una cadena.

Así que, ¿cómo puedo hacer que mi método devuelve un objeto de tipo, pero no el uso de la DataContractJsonSerializer? ¿Cómo puedo saber si el uso de la Json.Net serializador lugar?

O, ¿hay alguna manera escribir directamente a la secuencia de respuesta? Así que sólo puedo volver a la cruda json a mí mismo? Sin la envoltura de citas?

Aquí está mi ejemplo inventado, para la referencia:

[DataContract]
public class SimpleMessage
{
    [DataMember]
    public string Message { get; set; }
}

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class PersonService
{
    // uses DataContractJsonSerializer
    // returns {"Message":"Hello World"}
    [WebGet(UriTemplate = "helloObject")]
    public SimpleMessage SayHelloObject()
    {
        return new SimpleMessage("Hello World");
    }

    // uses Json.Net serialization, to return a json string
    // returns "{\"Message\":\"Hello World\"}"
    [WebGet(UriTemplate = "helloString")]
    public string SayHelloString()
    {
        SimpleMessage message = new SimpleMessage() { Message = "Hello World" };
        string json = JsonConvert.Serialize(message);
        return json;
    }

    // I need a mix of the two.  Return an object type, but use the Json.Net serializer.
}

38voto

Samuel Meacham Puntos 5058

Finalmente he encontrado una solución a esto. No es lo que yo habría preferido (que sería para devolver el tipo de objeto específico, y de alguna manera instruir WCF para utilizar un Json.Net serializador, en lugar de la DataContractJsonSerializer), pero es un gran trabajo, y es muy sencillo y claro.

Ampliando mi ejemplo inventado con esta nueva solución:

[WebGet(UriTemplate = "hello")]
public void SayHello()
{
    SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
    string json = JsonConvert.Serialize(message);
    HttpContext.Current.Response.ContentType = "application/json; charset=utf-8";
    HttpContext.Current.Response.Write(json);
}

Nota el tipo de retorno de void. Softonic no devolver nada, porque iba a ser serializado con DataContractJsonSerializer. En lugar de eso, me escriba directamente a la respuesta de la secuencia de salida. Dado que el tipo de retorno es void, la canalización de procesamiento no establece el tipo de contenido para el tipo predeterminado de "application/json", por lo que establecer de forma explícita.

Debido a que este utiliza HttpContext, supongo que solo funcionará si usted tiene [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] en su servicio de clase, ya que obligará a las solicitudes para el servicio de ir a través de la ASP.NET pipeline. Sin el asp.net la compatibilidad, la HttpContext no estará disponible, desde wcf hosting se supone para ser anfitrión agnóstico.

Usando este método, los resultados perfectos en firebug para peticiones GET. Correcto tipo de contenido, contenido correcto de longitud, y raw json, no envuelto en comillas. Y, me voy a la serialización quiero usar Json.Net. Mejor de ambos mundos.

No estoy 100% positiva de los obstáculos con que yo voy a correr sobre *de*la serialización, cuando mis métodos de servicio [DataContract] tipos de objetos como parámetros de entrada. Estoy suponiendo que el DataContractJsonSerializer será utilizado para eso. Cruzaremos ese puente cuando llegue yo a él...si se crea un problema. No tan lejos, con mi simple Dto.

ACTUALIZACIÓN Ver Oleg respuesta (el UPDATE2 parte). Él cambia el tipo de retorno del método de servicio de vacío a System.ServiceModel.Channels.Message, y en lugar de usar HttpContext.Current.Response.Write(), se utiliza:

return WebOperationContext.Current.CreateTextResponse (json,
    "application/json; charset=utf-8", Encoding.UTF8);

Que es de hecho una solución mejor. Gracias Oleg.

ACTUALIZACIÓN 2 Sin embargo, hay otra forma de lograr esto. Cambio de su servicio de devolución de tipo de Mensaje a transmitir, y devolver este:

WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json));

No he hecho ninguna prueba, pero es posible que esta sea una mejor opción para los métodos que podrían potencialmente retorno de grandes cantidades de datos. No sé si eso importa para los no-binario de datos. De todos modos, un pensamiento.

10voto

Oleg Puntos 136406

A mí me parece que el uso no correcto DataContractJsonSerializer. Lo que es extraño es: usted no definen ResponseFormat = ResponseFormat.Json de atributo para el public SimpleMessage SayHelloObject() método.

Por otra parte, si usted tiene {"Message":"Hello World"} en una cadena de texto y mostrarlo en el depurador será mostrar como "{\"Message\":\"Hello World\"}", exactamente igual que usted ve string json = JsonConvert.Serialize(message); (Json.Net). Así que me parece que en ambos casos el mismo resultado.

Para verificar este uso de un software de cliente que lea los resultados. Ver algunos ejemplos

http://stackoverflow.com/questions/2651091/jquery-ajax-call-to-httpget-webmethod-c-not-working/2656543#2656543

http://stackoverflow.com/questions/2670147/can-i-return-json-from-an-asmx-web-service-if-the-contenttype-is-not-json/2671583#2671583

http://stackoverflow.com/questions/2737525/how-do-i-build-a-json-object-to-send-to-an-ajax-webservice/2738086#2738086

ACTUALIZADO: En el código de definir el método SayHelloString(). Esto es el resultado son una cadena. Si se llama al método de esta cadena será una vez más JSON serie. Serialización JSON de la cadena {"Message":"Hello World"} es una cadena entre comillas (ver http://www.json.org/ definición no es un objeto, sino una cadena) o exactamente cadena "{\"Message\":\"Hello World\"}". Así que todo está correcto con ambos métodos del Servicio Web.

ACTUALIZADO 2: me alegro de que mi sugerencia de "Actualización" es parte de mi respuesta te haya ayudado a swich de la doble serialización JSON.

Sin embargo, yo recomendaría usted a cambio de un poco de la solución a permanecer más en el WCF concepto.

Si desea aplicar una codificación personalizada de la web de respuesta en WCF (ver http://msdn.microsoft.com/en-us/library/ms734675.aspx) su WCF método mejor que regresar Message en lugar de void:

[WebGet(UriTemplate = "hello")]
public Message SayHello()
{
    SimpleMessage message = new SimpleMessage() {Message = "Hello World"};
    string myResponseBody = JsonConvert.Serialize(message);
    return WebOperationContext.Current.CreateTextResponse (myResponseBody,
                "application/json; charset=utf-8",
                Encoding.UTF8);
}

Puede causar el uso de otro Mensaje formater: por ejemplo CreateStreamResponse (o algunos otros, a ver http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx) en lugar de CreateTextResponse. Si usted desea establecer algunas adicionales encabezados HTTP o Http de código de estado (por ejemplo en caso de algún error) usted puede hacer esto de esta manera:

OutgoingWebResponseContext ctx = WebOperationContext.Current.OutgoingResponse;
ctx.StatusCode = HttpStatusCode.BadRequest;

Al final quiero repetir mi pregunta en un comentario: ¿podría explicar por qué desea usar Json.Net en lugar de DataContractJsonSerializer? Es la mejora del rendimiento? ¿Necesita implementar la serialización de algunos tipos de datos como DateTime en otra forma como DataContractJsonSerializer hacer? O la razón principal de su elección de Json.Net algún otro?

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: