Подтвердить что ты не робот

Сделайте ASP.NET конвертер WCF в JSON, опустив теги "Key" и "Value"

Вот моя дилемма. Я использую службу 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;
}

Есть ли способ избавиться от тегов "Key" и "Value", используя только встроенные инструменты ASP.NET? (т.е. я бы предпочел не использовать JSON.NET, если это можно избежать)

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

4b9b3361

Ответ 1

Класс словаря .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] );
        }
    }

Ответ 2

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

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

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

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

Ответ 3

Я столкнулся с этой проблемой несколько месяцев назад и разместил здесь несколько менее оптимальный краткий вопрос: Настройка контракта данных 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"}

Объект expando работает как шарм. Но чтобы заставить его работать, вы должны заставить 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(что делает возвращение RESTful намного проще, чем WCF, IMO).

Ответ 4

Немного расширяя @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)));
     }
}

Ответ 5

избегая "__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/info/12858748/asp-net-webapi-posting-collection-of-subclasses
        //config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;