Jak Mogę zwrócić json z mojej usługi WCF rest (. NET 4), używając Json.Net, bez sznurka, zapakowanego w Cytaty?

Aktualizacja 10/19/2010 Wiem, że zadałem to pytanie jakiś czas temu, ale obejścia pokazane w tych odpowiedziach nie są zadowalające i nadal jest to częsty problem dla wielu. WCF po prostu nie jest elastyczne. Założyłem własną bibliotekę C# open source do tworzenia usług REST bez WCF. Sprawdź restcake.net lub rest.codeplex.com dla informacji o wspomnianej bibliotece. END UPDATE

Aktualizacja 8/2/2012 ASP.NET Web API (wcześniej WCF Web API, zamiennik dla Rest WCF) używa Json.NET domyślnie END UPDATE

DataContractJsonSerializer nie jest w stanie obsłużyć wielu scenariuszy, które Json.Net radzi sobie dobrze, gdy jest prawidłowo skonfigurowany(w szczególności cykle).

Metoda usługi może albo zwrócić określony typ obiektu (w tym przypadku DTO ), w którym to przypadku zostanie użyta DataContractJsonSerializer, albo mogę poprosić metodę o zwrócenie ciągu znaków i wykonanie serializacji samodzielnie za pomocą Json.Net. problem w tym, że gdy zwracam ciąg json w przeciwieństwie do obiektu, json wysyłany do klienta jest owinięty cudzysłowami.

Używając DataContractJsonSerializer, zwracając określony typ obiektu, odpowiedź jest:
{"Message":"Hello World"}

Za pomocą Json.Net aby zwrócić łańcuch json, odpowiedź jest:
"{\"Message\":\"Hello World\"}"

Nie chcę musieć eval () lub JSON.parse() wynik na kliencie, co musiałbym zrobić, jeśli json powróci jako ciąg znaków, owinięty cudzysłowami. Zdaję sobie sprawę, że zachowanie jest poprawne; po prostu nie jest to, czego chcę/potrzebuję. Potrzebuję surowego json; zachowanie, gdy typ zwracany przez metodę usług jest obiektem, a nie łańcuchem znaków.

Więc, jak moja metoda może zwrócić typ obiektu, ale nie używać DataContractJsonSerializer? Jak mogę powiedzieć, aby używać Json.Net serializer zamiast?

Czy Jest jakiś sposób, aby bezpośrednio napisać do strumienia odpowiedzi? Więc mogę sam zwrócić raw json? Bez owijania cytatów?

Oto mój wymyślony przykład, dla odniesienia:

[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.
}
Author: James Newton-King, 2010-06-12

2 answers

W końcu znalazłem na to rozwiązanie. Nie jest to, co bym wolał (czyli zwrócenie konkretnego typu obiektu, i jakoś polecić WCF używać Json.Net serializer, zamiast DataContractJsonSerializer), ale działa świetnie i jest prosty i przejrzysty.

Rozszerzenie mojego wymyślonego przykładu przy użyciu tego nowego rozwiązania:

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

Zwróć typ void. Nie zwracamy niczego, ponieważ byłoby to serializowane za pomocą DataContractJsonSerializer. Zamiast tego piszę bezpośrednio do strumienia wyjściowego odpowiedzi. Ponieważ typ zwracany jest void, potok przetwarzania nie ustawia typu content-type na domyślny typ "application / json", więc ustawiłem go jawnie.

Ponieważ to używa HttpContext, domyślam się, że zadziała tylko wtedy, gdy masz [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] na swojej klasie usług, ponieważ to wymusi żądania do serwisu, aby przejść przez ASP.NET rurociąg. Bez asp.net kompatybilności, HttpContext nie będzie dostępny, ponieważ hosting wcf jest miał być gospodarzem agnostykiem.

Używając tej metody, wyniki wyglądają idealnie w firebug dla żądań GET. Correct content-type, correct Content length, and raw json, not wrapped in quotes. I dostaję serializację, której chcę użyć Json.Net najlepsze z obu światów.

Nie jestem w 100% pewien, na jakie przeszkody mogę napotkać w odniesieniu do serializacji de, gdy moje metody usługowe mają jako parametry wejściowe typy obiektów [DataContract]. Zakładam, że Do tego również zostanie użyty DataContractJsonSerializer. Przejdę przez ten most, kiedy do niego dojdę...jeśli to stwarza problem. Do tej pory nie, z moim prostym DTOs.

Aktualizacja Zobacz odpowiedź Olega (część UPDATE2). Zmienia typ zwracanej metody z void na System.ServiceModel.Channels.Message i zamiast używać HttpContext.Current.Response.Write(), używa:

return WebOperationContext.Current.CreateTextResponse (json,
    "application/json; charset=utf-8", Encoding.UTF8);
Co jest rzeczywiście lepszym rozwiązaniem. Dziękuję, Oleg.

Aktualizacja 2 Jest jeszcze inny sposób, aby to osiągnąć. Zmień Typ powrotu usługi z Wiadomości do strumienia i zwraca to:

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

Nie robiłem żadnych konkretnych testów, ale możliwe, że byłby to lepszy wybór dla metod, które mogłyby potencjalnie zwrócić duże ilości danych. Nie wiem, czy to ma znaczenie dla danych niebinarnych. W każdym razie, myśl.

 37
Author: Samuel Meacham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-15 18:17:19

Wydaje mi się, że używasz nieprawidłowego DataContractJsonSerializer. Dziwne jest to, że nie definiujesz atrybutu ResponseFormat = ResponseFormat.Json dla metody public SimpleMessage SayHelloObject().

Ponadto, jeśli masz {"Message":"Hello World"} w łańcuchu i wyświetlasz go w debuggerze, zostanie on wyświetlony jako "{\"Message\":\"Hello World\"}", więc dokładnie tak, jak widzisz string json = JsonConvert.Serialize(message); (Json.Net). więc wydaje mi się, że masz w obu przypadkach takie same wyniki.

Aby to sprawdzić, użyj oprogramowania klienckiego, które odczytuje wyniki. Zobacz przykłady

Wywołanie jQuery ajax do httpget webmethod (c#) nie działa

Czy Mogę zwrócić JSON z an .asmx Web Service jeśli ContentType nie jest JSON?

Jak zbudować obiekt JSON do wysłania do AJAX WebService?

Aktualizacja: w kodzie definiujesz metodę SayHelloString(). Jego wynikiem jest ciąg. Jeśli wywołasz metodę, ten ciąg znaków będzie Jeszcze raz JSON serializowany. JSON serialization of the string {"Message":"Hello World"} is a quoted string (see http://www.json.org / definicja dla Nie obiekt, ale łańcuch) lub dokładnie string "{\"Message\":\"Hello World\"}". Tak więc wszystko jest poprawne z obiema metodami twojego serwisu internetowego.

Aktualizacja 2: cieszę się, że moja wskazówka z części "aktualizacja" mojej odpowiedzi pomogła Ci swich podwójnej serializacji JSON.

Niemniej jednak polecam Ci zmienić trochę rozwiązanie, aby pozostać bardziej przy koncepcji WCF.

Jeśli chcesz zaimplementować własne kodowanie web responce w WCF (zobacz http://msdn.microsoft.com/en-us/library/ms734675.aspx) twoja metoda WCF powinna lepiej zwracać Message zamiast 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);
}

Możesz użyć innego Formatera wiadomości: na przykład CreateStreamResponse (lub innego patrz http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx) zamiast CreateTextResponse. Jeśli chcesz ustawić dodatkowe nagłówki HTTP lub kod statusu Http (na przykład w przypadku błędu), możesz to zrobić za pomocą w ten sposób:

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

Na koniec chcę powtórzyć moje pytanie z komentarza: czy mógłbyś wyjaśnić dlaczego chcesz używać Json.Net zamiast DataContractJsonSerializer? Czy to poprawa wydajności? Czy potrzebujesz zaimplementować serializację niektórych typów danych, takich jak DateTime w inny sposób jak DataContractJsonSerializer? Czy głównym powodem wyboru Json.Net jest jakiś inny?

 10
Author: Oleg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:25:02