40 votes

Comment puis-je renvoyer JSON à partir de mon service de repos WCF (.NET 4), à l'aide de Json.Net, sans qu'il s'agisse d'une chaîne, entourée de guillemets?

Mise à JOUR 10/19/2010 Je sais que j'ai posé cette question il y a longtemps, mais les solutions de contournement indiqué dans la réponse à ces questions ne sont guère satisfaisantes, et c'est toujours un problème commun pour beaucoup. WCF n'est pas flexible. J'ai commencé ma propre open source C# bibliothèque pour la création de services REST sans WCF. Vérifier restcake.net ou rest.codeplex.com pour info sur ladite bibliothèque. MISE À JOUR DE FIN

Mise à JOUR 8/2/2012 ASP.NET l'API Web (précédemment Web WCF API, le remplacement pour le RESTE de la WCF) utilise Json.NET par défaut MISE À JOUR DE FIN

L' DataContractJsonSerializer est incapable de gérer de nombreux scénarios Json.Net poignées juste d'amende lorsqu'il est correctement configuré (en particulier, les cycles).

Une méthode de service peut renvoyer un type d'objet spécifique (dans ce cas, un DTO), auquel cas l' DataContractJsonSerializer sera utilisé, ou je peux avoir la méthode renvoie une chaîne de caractères, et de faire de la sérialisation moi-même avec Json.Net. Le problème est que quand je retourne une chaîne json, par opposition à un objet, le json qui est envoyé au client est enveloppé dans des guillemets.

À l'aide de DataContractJsonSerializer, en retournant un type d'objet spécifique, la réponse est:
{"Message":"Hello World"}

À l'aide de Json.Net pour retourner une chaîne json, la réponse est:
"{\"Message\":\"Hello World\"}"

Je ne veux pas avoir à eval() ou JSON.parse() le résultat sur le client, qui est ce que j'aurais à faire si le json revient comme une chaîne de caractères, enveloppé dans les citations. Je me rends compte que le comportement est correct, c'est juste pas ce que j'ai envie/besoin. J'ai besoin de la crue json; le comportement lorsque la méthode de service de type de retour est un objet, pas une chaîne de caractères.

Alors, comment puis-je avoir ma méthode retourne un objet de type, mais de ne pas utiliser le DataContractJsonSerializer? Comment puis-je lui dire d'utiliser le Json.Net sérialiseur à la place?

Ou, est-il d'une certaine manière d'écrire directement le flux de réponse? Si je peux juste de retour de la crue json moi-même? Sans l'emballage d'un devis?

Voici mon exemple artificiel, pour référence:

[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.
}

40voto

Samuel Meacham Points 5058

J'ai enfin trouvé une solution à cette question. Ce n'est pas ce que j'aurais préféré (qui serait de retour le type d'objet spécifique, et en quelque sorte instruire WCF pour utiliser un Json.Net sérialiseur, au lieu de la DataContractJsonSerializer), mais il fonctionne très bien, et c'est simple et clair.

L'extension de mon exemple artificiel à l'aide de cette nouvelle solution:

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

Remarque le type de retour d' void. Nous ne retourne rien, car il faudrait être sérialisé avec DataContractJsonSerializer. Au lieu de cela, j'écris directement à la réponse au flux de sortie. Depuis le type de retour est void, le pipeline de traitement n'est pas de définir le type de contenu par défaut de type "application/json", alors je l'ai mis explicitement.

Parce que c'utilise HttpContext, j'imagine qu'il ne fonctionnera que si vous avez [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] sur votre classe de service, étant donné que la force de demandes de service à passer par la ASP.NET pipeline. Sans l'asp.net la compatibilité, la HttpContext ne sera pas disponible, depuis wcf hébergement est censé être l'hôte agnostique.

En utilisant cette méthode, le résultat est parfait dans firebug pour les requêtes GET. Correct type de contenu, corriger la longueur du contenu, et des matières premières json, pas enveloppé dans des guillemets. Et, je me fais de la sérialisation je veux de l'aide Json.Net. Le meilleur des deux mondes.

Je ne suis pas 100% positif de quels sont les obstacles que je pourrais exécuter en matière *de*la sérialisation, lors de mon service méthodes ont [DataContract] types d'objets comme paramètres d'entrée. Je suis en supposant que le DataContractJsonSerializer seront utilisés pour cela. Allons traverser ce pont quand je reviendrai...si cela pose un problème. Il n'est pas si loin, avec mon simple Otd.

Mise à JOUR Voir Oleg réponse (le UPDATE2 partie). Il change le type de retour de la méthode de service de void à l' System.ServiceModel.Channels.Message, et plutôt que d'utiliser HttpContext.Current.Response.Write(), qu'il utilise:

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

Qui est en effet une meilleure solution. Merci Oleg.

Mise à JOUR 2 Il y a encore une autre façon d'accomplir ceci. Changement de votre service type de retour de Message à diffuser, et de renvoyer ce:

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

Je n'ai pas fait de tests, mais il est possible que ce serait un meilleur choix pour les méthodes qui pourraient potentiellement retour de grandes quantités de données. Je ne sais pas si ce qui compte pour les non-binaire de données si. De toute façon, une pensée.

11voto

Oleg Points 136406

Il me semble que vous utilisez pas correcte DataContractJsonSerializer. Ce qui est étrange, c'est: vous ne définissez pas d' ResponseFormat = ResponseFormat.Json de l'attribut de l' public SimpleMessage SayHelloObject() méthode.

D'ailleurs si vous avez {"Message":"Hello World"} dans une chaîne de caractères et l'afficher dans le débogueur, il sera afficher en tant que "{\"Message\":\"Hello World\"}", donc exactement comme vous le voyez string json = JsonConvert.Serialize(message); (Json.Net). Il me semble donc que vous avez dans les deux cas, les mêmes résultats.

Pour vérifier cela, utilisez un logiciel client qui a lu les résultats. Voir quelques exemples

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

Mise à JOUR: Dans votre code, vous définissez la méthode d' SayHelloString(). C'est le résultat sont d'une chaîne. Si vous appelez la méthode de cette chaîne sera une fois de plus JSON sérialisé. La sérialisation JSON de la chaîne {"Message":"Hello World"} est une chaîne de caractères entre guillemets (voir http://www.json.org/ définition n'est pas un objet, mais une chaîne de caractères) ou exactement chaîne "{\"Message\":\"Hello World\"}". Si tout est correct avec les deux méthodes de votre Service Web.

Mise à JOUR 2: je suis heureux que mon bout de "mettre à Jour" une partie de ma réponse vous a aidé à swich de la double sérialisation JSON.

Néanmoins, je vous recommande de changer un peu de la solution de rester plus à la WCF concept.

Si vous voulez mettre en oeuvre un codage personnalisé du web responce dans WCF (voir http://msdn.microsoft.com/en-us/library/ms734675.aspx) votre WCF méthode devrait améliorer le rendement Message au lieu 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);
}

Vous pouvez de cause l'utilisation d'un autre Message formateur de table: par exemple CreateStreamResponse (ou une autre voir http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx) au lieu de CreateTextResponse. Si vous souhaitez définir quelques autres en-têtes HTTP ou Http status code (par exemple dans le cas d'une erreur) vous pouvez le faire de cette manière:

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

À la fin, je veux répéter ma question à partir d'un commentaire: pourriez-vous expliquer pourquoi vous souhaitez utiliser Json.Net au lieu de DataContractJsonSerializer? Est-il l'amélioration de la performance? Avez-vous besoin de mettre en œuvre la sérialisation de certains types de données, comme DateTime d'une autre manière en tant que DataContractJsonSerializer faire? Ou la principale raison de votre choix de la Json.Net est quelques autres?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X