开发者

Make ASP.NET WCF convert dictionary to JSON, omitting "Key" & "Value" tags

开发者 https://www.devze.com 2023-04-08 22:22 出处:网络
Here\'s my dilemma.I\'m using a RESTful ASP.NET service, trying to get a function to return a JS开发者_JAVA百科ON string in this format:

Here's my dilemma. I'm using a RESTful ASP.NET service, trying to get a function to return a JS开发者_JAVA百科ON string in this format:

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

But I'm getting it in this format instead:

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

My method looks like this:

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

Is there any way I can get rid of those "Key" and "Value" tags using only built in ASP.NET tools? (i.e., I'd rather not use JSON.NET, if it's avoidable)

Thanks very much! :)


The .NET dictionary class won't serialize any other way than the way you described. But if you create your own class and wrap the dictionary class then you can override the serializing/deserializing methods and be able to do what you want. See example below and pay attention to the "GetObjectData" method.

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


Expanding slightly on @MarkisT's excellent solution, you can modify the serialization constructor to recreate one of these dictionaries from the same JSON (thus allowing you to take an AjaxDictionary as a service parameter), as follows:

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


In case anyone has that problem on the client side: conversion from that weird {Key: "x", Value:"y"} Array to a { x: "y" } object can be done in a single line of JS:

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

with i being the array returned from the service, and o being what you actually want.

best regards


I ran up against this problem a number of months ago and posted a somewhat less-than-optimally succinct question here: Configuring WCF data contract for proper JSON response

The problem I had back then turned out to be same as the much more precisely posted question here, in short: within the context of WCF the standard asp.net serialization tools will, for a dictionary, return an ARRAY rather than a key/value pair json OBJECT. I am posting my solution which worked for me although I did resort to using JSON.NET (which I realize the poster was trying to avoid). Nevertheless, maybe this will be helpful to someone.

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

The result:

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

The expando object works like a charm. But to make it work you have to force WCF to return plain text which one would think is easy but it is not. You have to implement a RawContentTypeMapper as suggested here: http://referencesource.microsoft.com/#System.ServiceModel.Web/System/ServiceModel/Channels/RawContentTypeMapper.cs ...And then you have to mess around with your web.config file something like this:

   <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>

I am the first to admit that this solution will likely not receive any awards for elegance. But it worked and returning raw content from a WCF webservice will, if needed, give you some extra control how to serialize your WCF data payload. Since implementing this, I have migrated more and more to ASP.NET Web API (which makes returning RESTful anything much easier than WCF, IMO).


avoiding the "__type" in json...

in the webapi.config, there are several options (look to the last one):

        // 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;
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号