Как я могу вернуть json из моей службы отдыха WCF (.NET 4), используя Json.Net, без строки, заключенной в кавычки? - PullRequest
40 голосов
/ 12 июня 2010

ОБНОВЛЕНИЕ 10/19/2010 Я знаю, что задал этот вопрос некоторое время назад, но обходные пути, показанные в этих ответах, вряд ли являются удовлетворительными, и это все еще является общей проблемой для многих.WCF просто не гибкийЯ начал свою собственную библиотеку C # с открытым исходным кодом для создания служб REST без WCF.Проверьте restcake.net или rest.codeplex.com для получения информации об указанной библиотеке. END UPDATE

ОБНОВЛЕНИЕ 8/2/2012 ASP.NET Web API (ранее использовался WCF Web API, замена для REST WCF) Json.NET по умолчанию КОНЕЦ ОБНОВЛЕНИЯ

DataContractJsonSerializer не может обрабатывать многие сценарии, которые Json.Net обрабатывает очень хорошо, когда правильносконфигурирован (в частности, циклы).

Сервисный метод может либо возвращать определенный тип объекта (в данном случае DTO ), в этом случае будет использоваться DataContractJsonSerializer, либо Iможет иметь метод, возвращающий строку, и выполнить сериализацию самостоятельно с помощью Json.Net.Проблема в том, что когда я возвращаю строку json, а не объект, json, отправляемый клиенту, заключается в кавычки.

Используя DataContractJsonSerializer, возвращая определенный тип объекта, получается ответ:
{"Message":"Hello World"}

Использование Json.Net для возврата строки json, ответ:
"{\"Message\":\"Hello World\"}"

Я не хочу иметь eval () или JSON.parse () результат на клиенте, что я должен был бы сделать, если json вернется в виде строки, заключенной в кавычки.Я понимаю, что поведение правильное;это просто не то, что я хочу / нужен.Мне нужен сырой JSON;поведение, когда тип возвращаемого служебного метода является объектом, а не строкой.

Итак, как я могу заставить мой метод возвращать тип объекта, но не использует DataContractJsonSerializer?Как я могу сказать ему использовать вместо этого сериализатор Json.Net?

Или есть какой-то способ прямой записи в поток ответов?Так что я могу просто вернуть сырой JSON сам?Без кавычек?

Вот мой надуманный пример, для справки:

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

Ответы [ 2 ]

40 голосов
/ 12 июня 2010

Я наконец-то нашел решение для этого.Это не то, что я бы предпочел (который должен был бы возвращать определенный тип объекта и каким-то образом инструктировать WCF использовать сериализатор Json.Net вместо DataContractJsonSerializer), но он работает отлично, и это просто и понятно.

Расширение моего надуманного примера с использованием этого нового решения:

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

Обратите внимание на тип возврата void.Мы ничего не возвращаем, так как это будет сериализовано с DataContractJsonSerializer.Вместо этого я пишу прямо в поток вывода ответа.Так как тип возвращаемого значения void, конвейер обработки не устанавливает тип содержимого в тип по умолчанию "application / json", поэтому я установил его явно.

, поскольку при этом используется HttpContext, I 'Я предполагаю, что это будет работать, только если у вас есть [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] в вашем классе обслуживания, так как это заставит запросы к службе проходить через конвейер ASP.NET.Без совместимости с asp.net HttpContext не будет доступен, поскольку предполагается, что хостинг wcf не зависит от хоста.

При использовании этого метода результаты выглядят идеально в firebug для запросов GET.Правильный тип контента, правильная длина контента и необработанный json, без кавычек.И я получаю сериализацию, которую я хочу, используя Json.Net.Лучшее из двух миров.

Я не на 100% уверен в том, с какими препятствиями я могу столкнуться при сериализации de , когда мои методы обслуживания имеют в качестве входных параметров типы объектов [DataContract].Я предполагаю, что DataContractJsonSerializer будет использоваться для этого тоже.Перейду тот мост, когда я приду к нему ... если это создаст проблему.Пока нет, с моими простыми DTO.

ОБНОВЛЕНИЕ См. Ответ Олега (часть ОБНОВЛЕНИЕ2).Он меняет тип возвращаемого значения метода обслуживания с void на System.ServiceModel.Channels.Message, и вместо использования HttpContext.Current.Response.Write() он использует:

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

, что действительно является лучшим решением.Спасибо, Олег.

ОБНОВЛЕНИЕ 2 Есть еще один способ сделать это.Измените тип возврата вашего сервиса с Message на Stream и верните это:

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

Я не проводил никаких специальных тестов, но возможно, что это был бы лучший выбор для методов, которые потенциально могут возвращать большие суммыданных.Я не знаю, имеет ли это значение для недвоичных данных, хотя.Во всяком случае, мысль.

11 голосов
/ 12 июня 2010

Мне кажется, что вы используете не правильно DataContractJsonSerializer.Что странно: вы не определяете атрибут ResponseFormat = ResponseFormat.Json для метода public SimpleMessage SayHelloObject().

Более того, если у вас есть {"Message":"Hello World"} в строке и вы увидите ее в отладчике, она будет отображаться как "{\"Message\":\"Hello World\"}"так же, как вы видите string json = JsonConvert.Serialize(message); (Json.Net).Поэтому мне кажется, что у вас в обоих случаях одинаковые результаты.

Для проверки используйте клиентское программное обеспечение, которое считывает результаты.См. Некоторые примеры

AJAX-вызов JQuery для httpget webmethod (c #) не работает

Могу ли я вернуть JSON из веб-службы .asmx, если ContentType не являетсяJSON?

Как создать объект JSON для отправки в AJAX WebService?

ОБНОВЛЕНО : В своем коде вы определяетеметод SayHelloString().Это результат строки.Если вы вызовете метод, эта строка будет еще раз JSON-сериализация.JSON-сериализация строки {"Message":"Hello World"} - строка в кавычках (см. Определение http://www.json.org/ не для объекта, а для строки) или точно строка "{\"Message\":\"Hello World\"}".Итак, все правильно с обоими методами вашего веб-сервиса.

ОБНОВЛЕНО 2 : Я рад, что мой совет из части «Обновление» моего ответа помог вам выбрать двойную сериализацию JSON.

Тем не менее, я бы порекомендовал вам немного изменить решение, чтобы больше придерживаться концепции WCF.

Если вы хотите реализовать пользовательскую кодировку веб-ответа в WCF (см. http://msdn.microsoft.com/en-us/library/ms734675.aspx) ваш метод WCF должен лучше возвращать Message вместо 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);
}

Вы можете использовать другой формирователь сообщений: например CreateStreamResponse (или какой-либо другой см. http://msdn.microsoft.com/en-us/library/system.servicemodel.web.weboperationcontext_methods(v=VS.100).aspx) вместо CreateTextResponse. Если вы хотите установить дополнительные заголовки HTTP или код статуса Http (например, в случае какой-либо ошибки), вы можете сделать это следующим образом:

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

В концеЯ хочу повторить мой вопрос из комментария: не могли бы вы объяснить, почему вы хотите использовать Json.Net вместо DataContractJsonSerializer? Это улучшение производительности? Вам нужно реализовать сериализацию некоторых типов данных, таких как DateTime, другим способом, как DataContractJsonSerializer делать?Или главная причина вашего выбора Json.Net - какая-то другая?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...