¿Cómo devuelvo JSON limpio de un servicio WCF?

233

Estoy tratando de devolver algo de JSON de un servicio WCF. Este servicio simplemente devuelve parte del contenido de mi base de datos. Puedo obtener los datos. Sin embargo, me preocupa el formato de mi JSON. Actualmente, el JSON que se devuelve tiene el siguiente formato:

{"d":"[{\"Age\":35,\"FirstName\":\"Peyton\",\"LastName\":\"Manning\"},{\"Age\":31,\"FirstName\":\"Drew\",\"LastName\":\"Brees\"},{\"Age\":29,\"FirstName\":\"Tony\",\"LastName\":\"Romo\"}]"} 

En realidad, me gustaría que mi JSON tenga el formato más limpio posible. Creo (puedo ser incorrecto) que la misma colección de resultados, representada en JSON limpio, debería verse así:

[{
  "Age": 35,
  "FirstName": "Peyton",
  "LastName": "Manning"
}, {
  "Age": 31,
  "FirstName": "Drew",
  "LastName": "Brees"
}, {
  "Age": 29,
  "FirstName": "Tony",
  "LastName": "Romo"
}]

No tengo idea de dónde viene la "d". Tampoco tengo idea de por qué se están insertando los caracteres de escape. Mi entidad tiene el siguiente aspecto:

[DataContract]
public class Person
{
    [DataMember]
    public string FirstName { get; set; }

    [DataMember]
    public string LastName { get; set; }

    [DataMember]
    public int Age { get; set; }

    public Person(string firstName, string lastName, int age)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
        this.Age = age;
    }
}

El servicio responsable de devolver el contenido se define como:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json)]
    public string GetResults()
    {
        List<Person> results = new List<Person>();
        results.Add(new Person("Peyton", "Manning", 35));
        results.Add(new Person("Drew", "Brees", 31));
        results.Add(new Person("Tony", "Romo", 29));

        // Serialize the results as JSON
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(results.GetType());
        MemoryStream memoryStream = new MemoryStream();
        serializer.WriteObject(memoryStream, results);

        // Return the results serialized as JSON
        string json = Encoding.Default.GetString(memoryStream.ToArray());
        return json;
    }
}

¿Cómo devuelvo JSON "limpio" de un servicio WCF? ¡Gracias!

usuario208662
fuente
SOAP debe devolver XML. Puede usar un punto final REST para devolver JSON. Eche un vistazo stackoverflow.com/questions/186631/…
Akira Yamamoto
44
Por cierto, si alguien más se encuentra con esto y se pregunta por qué está la propiedad "d", está allí para parchear una vulnerabilidad JSON . Eliminarlo te vuelve vulnerable de nuevo.
Alex
44
@Alex: esa vulnerabilidad depende de la redefinición del objeto Array, que ya no es posible en los navegadores modernos. Ver stackoverflow.com/questions/16289894/…
Cheeso
Eso es bueno. :) Sin embargo, la mitad de mi respuesta sigue siendo cierta: estaba allí para corregir esa vulnerabilidad.
Alex

Respuestas:

213

Cambia el tipo de retorno de tus GetResults para que sea List<Person>.
Elimine el código que usa para serializar la Lista en una cadena json; WCF lo hace automáticamente.

Usando su definición para la clase Persona, este código funciona para mí:

public List<Person> GetPlayers()
{
    List<Person> players = new List<Person>();
    players.Add(new  Person { FirstName="Peyton", LastName="Manning", Age=35 } );
    players.Add(new  Person { FirstName="Drew", LastName="Brees", Age=31 } );
    players.Add(new  Person { FirstName="Brett", LastName="Favre", Age=58 } );

    return players;
}

resultados:

[{"Age":35,"FirstName":"Peyton","LastName":"Manning"},  
 {"Age":31,"FirstName":"Drew","LastName":"Brees"},  
 {"Age":58,"FirstName":"Brett","LastName":"Favre"}]

(Todo en una línea)

También usé este atributo en el método:

[WebInvoke(Method = "GET",
           RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "players")]

WebInvoke with Method = "GET" es lo mismo que WebGet, pero como algunos de mis métodos son POST, utilizo todo WebInvoke para mantener la coherencia.

UriTemplate establece la URL en la que está disponible el método. Entonces puedo hacer un GET http://myserver/myvdir/JsonService.svc/playersy simplemente funciona.

También revise IIRF u otra reescritura de URL para deshacerse del .svc en el URI.

Cheeso
fuente
Cheeso: probé este enfoque antes de publicar esta pregunta. Cuando uso este enfoque, aparece un error que dice "Los puntos finales que usan 'UriTemplate' no se pueden usar con 'System.ServiceModel.Description.WebScriptEnablingBehavior'". ¿Qué estoy haciendo mal? ¡Gracias!
user208662
28
use <webHttp /> en lugar de <webScriptEnablingBehavior /> en su archivo .config.
Cheeso
99
OK, reemplacé <enableWebScript /> con <webHttp /> y funcionó.
MGOwen
3
MGowen - Para su información, la mejor opción al hacer una nueva pregunta es ... abrir una nueva pregunta, en lugar de publicar la pregunta como un comentario a una respuesta anterior.
Cheeso
55
Favre ve lo que hiciste allí.
ruffin
93

Si desea un buen JSON sin atributos de codificación en sus clases de servicio,

utilizar <webHttp defaultOutgoingResponseFormat="Json"/>en su configuración de comportamiento

JeremyWeir
fuente
8

Me enfrenté al mismo problema y lo resolví cambiando el valor de atributo BodyStyle a "WebMessageBodyStyle.Bare":

[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetProjectWithGeocodings/{projectId}")]
GeoCod_Project GetProjectWithGeocodings(string projectId);

El objeto devuelto ya no se envolverá.

KhalilG
fuente
1

Cuando utiliza el método GET, el contrato debe ser este.

[WebGet(UriTemplate = "/", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
List<User> Get();

con esto tenemos un json sin el parámetro de arranque

Aldo Flores @alduar http://alduar.blogspot.com

alduar
fuente
1

En su IServece.cs agregue la siguiente etiqueta: BodyStyle = WebMessageBodyStyle.Bare

 [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "Getperson/{id}")]

    List<personClass> Getperson(string id);
Osama Ibrahim
fuente
¿Puedes explicar también por qué BodyStyle puede afectar el resultado?
MBH