Сделайте ASP.NET WCF для преобразования словаря в JSON, пропуская теги «Ключ» и «Значение» - PullRequest
29 голосов
/ 29 сентября 2011

Вот моя дилемма. Я использую сервис RESTful ASP.NET, пытаясь получить функцию, возвращающую строку JSON в следующем формате:

{"Test1Key":"Test1Value","Test2Key":"Test2Value","Test3Key":"Test3Value"}

Но вместо этого я получаю его в следующем формате:

[{"Key":"Test1Key","Value":"Test1Value"},
{"Key":"Test2Key","Value":"Test2Value"},
{"Key":"Test3Key","Value":"Test3Value"}]

Мой метод выглядит так:

[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public Dictionary<string, string> Test(String Token)
{
    if (!IsAuthorized(Token))
        return null;

    if (!IsSecure(HttpContext.Current))
        return null;

    Dictionary<string, string> testresults = new Dictionary<string, string>();
    testresults.Add("Test1Key", "Test1Value");
    testresults.Add("Test2Key", "Test2Value");
    testresults.Add("Test3Key", "Test3Value");
    return testresults;
}

Можно ли как-нибудь избавиться от этих тегов "Ключ" и "Значение", используя только встроенные инструменты ASP.NET? (т.е. я бы предпочел не использовать JSON.NET, если этого можно избежать)

Большое спасибо! :)

Ответы [ 5 ]

43 голосов
/ 29 сентября 2011

Класс словаря .NET не будет сериализован иначе, чем вы описали.Но если вы создаете свой собственный класс и переносите класс словаря, то вы можете переопределить методы сериализации / десериализации и иметь возможность делать то, что вы хотите.Смотрите пример ниже и обратите внимание на метод «GetObjectData».

    [Serializable]
    public class AjaxDictionary<TKey, TValue> : ISerializable
    {
        private Dictionary<TKey, TValue> _Dictionary;
        public AjaxDictionary()
        {
            _Dictionary = new Dictionary<TKey, TValue>();
        }
        public AjaxDictionary( SerializationInfo info, StreamingContext context )
        {
            _Dictionary = new Dictionary<TKey, TValue>();
        }
        public TValue this[TKey key]
        {
            get { return _Dictionary[key]; }
            set { _Dictionary[key] = value; }
        }
        public void Add(TKey key, TValue value)
        {
            _Dictionary.Add(key, value);
        }
        public void GetObjectData( SerializationInfo info, StreamingContext context )
        {
            foreach( TKey key in _Dictionary.Keys )
                info.AddValue( key.ToString(), _Dictionary[key] );
        }
    }
3 голосов
/ 08 ноября 2017

Слегка расширяясь от превосходного решения @ MarkisT , вы можете изменить конструктор сериализации, чтобы воссоздать один из этих словарей из того же JSON (что позволит вам принять AjaxDictionary в качестве параметра службы), следующим образом:

public AjaxDictionary( SerializationInfo info, StreamingContext context )
{
     _Dictionary = new Dictionary<TKey, TValue>();

     foreach (SerializationEntry kvp in info)
     {
         _Dictionary.Add((TKey)Convert.ChangeType(kvp.Name, typeof(TKey)), (TValue)Convert.ChangeType(kvp.Value, typeof(TValue)));
     }
}
2 голосов
/ 30 декабря 2014

Я столкнулся с этой проблемой несколько месяцев назад и разместил здесь несколько менее оптимальный вопрос: Настройка контракта данных WCF для правильного ответа JSON

Проблема, с которой я столкнулся в то время, оказалась такой же, как и гораздо более точно заданный здесь вопрос, вкратце: в контексте WCF стандартные инструменты сериализации asp.net для словаря будут возвращать ARRAY, а не пара ключ / значение json OBJECT. Я публикую свое решение, которое сработало для меня, хотя я и прибег к использованию JSON.NET (чего, как я понимаю, автор постарался избежать). Тем не менее, может быть, это будет кому-то полезно.

Function myDictionaryFunction () As Stream Implements IMywebservice.myDictionaryFunction
   Dim myKeyValuePairObject As Object = New Dynamic.ExpandoObject
   Dim myDictionary = DirectCast(myKeyValuePairObject, IDictionary(Of String, Object))
   myDictionary.Add("Test1Key", "Test1Value")
   myDictionary.Add("Test2Key", "Test2Value")
   myDictionary.Add("Test3Key", "Test3Value")


   strJson = JsonConvert.SerializeObject(myKeyValuePairObject)
   Dim resultBytes As Byte() = Encoding.UTF8.GetBytes(strJson)
   WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain"

   Return New MemoryStream(resultBytes)


End Function

Результат:

{"Test1Key":"Test1Value","Test2Key":"Test2Value","Test3Key":"Test3Value"}

Объект эксплорера работает как шарм. Но чтобы это сработало, вы должны заставить WCF возвращать простой текст, который кажется простым, но это не так. Вы должны реализовать RawContentTypeMapper как предложено здесь: http://referencesource.microsoft.com/#System.ServiceModel.Web/System/ServiceModel/Channels/RawContentTypeMapper.cs ... А потом вам придется возиться с файлом web.config примерно так:

   <customBinding>
    <binding name="RawReceiveCapable">
      <webMessageEncoding
        webContentTypeMapperType="myNamespace.RawContentTypeMapper, myLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <httpTransport manualAddressing="true" maxReceivedMessageSize="524288000" transferMode="Streamed" />
    </binding>
  </customBinding>

Я первый, кто признает, что это решение, скорее всего, не получит никаких наград за элегантность. Но это сработало, и возврат необработанного контента из веб-службы WCF при необходимости даст вам дополнительный контроль над сериализацией полезной нагрузки данных WCF. С тех пор как я это реализовал, я все больше и больше переходил на ASP.NET Web API (что делает возврат RESTful намного проще, чем WCF, IMO).

2 голосов
/ 20 октября 2014

В случае, если у кого-то есть такая проблема на стороне клиента: преобразование из этого странного {Key: "x", Value: "y"} массива в объект {x: "y"} может быть выполнено в одной строкеJS:

var o = i.reduce(function (p, c, a, i) { p[c.Key] = c.Value; return p }, {});

, где i - это массив, возвращаемый службой, а o - то, что вы на самом деле хотите.

наилучшие пожелания

0 голосов
/ 04 марта 2014

избегая "__type" в json ...

в webapi.config, есть несколько опций (посмотрите на последний):

        // To disable tracing in your application, please comment out or remove the following line of code
        // For more information, refer to: http://www.asp.net/web-api
        //config.EnableSystemDiagnosticsTracing();

        // Use camel case for JSON data.
        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        // The setting will let json.net to save type name in the payload if the runtime type is different with the declare type. 
        // When you post it back, json.net will deserialize the payload to the type you specified in the payload.
        // source: http://stackoverflow.com/questions/12858748/asp-net-webapi-posting-collection-of-subclasses
        //config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...