Make ASP.NET Wcf Convert Dictionary to JSON, Omitting "Key" & "Value" Tags

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

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

Serialize a Dictionarystring,Type and remove key/value from JSON string

It's not fun, but try this:

[Test]
public void Json()
{
var input = @"{
""Description"":""test"",
""RoomTypes"":[
{
""Key"":""A"",
""Value"":{
""Name"":""Delux""
}
},
{
""Key"":""B"",
""Value"":{
""Name"":""Non delux""
}
}
],
""Url"":""http:\/\/test.com""
}";

var temp = JsonConvert.DeserializeObject<Temp>(input);
var transform = new Transform
{
Description = temp.Description,
Url = temp.Url,
RoomTypes = new List<IDictionary<string, Temp.NameObj>>()
};

foreach (var group in temp.RoomTypes)
{
var dic = new Dictionary<string, Temp.NameObj> {{@group.Key, @group.Value}};
transform.RoomTypes.Add(dic);
}

Console.WriteLine(JsonConvert.SerializeObject(transform));
}

public class Transform
{
public string Description { get; set; }
public IList<IDictionary<string, Temp.NameObj>> RoomTypes { get; set; }
public string Url { get; set; }
}

public class Temp
{
public string Description { get; set; }
public IList<GroupObj> RoomTypes { get; set; }
public string Url { get; set; }

public class GroupObj
{
public string Key { get; set; }
public NameObj Value { get; set; }
}

public class NameObj
{
public string Name { get; set; }
}
}

The idea is to use Json.Net's dictionary serialization to achieve the structure you want.

Remove internal __type tags in ASP.NET Restful WCF

Okay, fixed it up - but ended up doing things a little differently. WCF seems to insist on putting in those internal "__type" tags when it serializes Dictionaries, but for some reason, it doesn't do the same thing with Streams.

Here's the new webservice method code (just the return type has changed):

[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public Stream Test1(String Token, String StreamId)
{
DataTable testTable = new DataTable();
testTable.Columns.Add("Key1", typeof(System.String));
testTable.Columns.Add("Key2", typeof(System.String));
testTable.Columns.Add("Key3", typeof(System.String));

DataRow testRow = testTable.NewRow();
testRow["Key1"] = "Value1";
testRow["Key2"] = "Value2";
testRow["Key3"] = "Value3";

testTable.Rows.Add(testRow);

return SuccessfulResult(testTable);
}

And here's the new SuccessfulResult function (which is what made the difference):

private Stream SuccessfulResult(DataTable dt = null)
{
Dictionary<string, object> returnDict = new Dictionary<string, object>();
returnDict.Add("Status", "Success");

Dictionary<string,object> dct = new Dictionary<string,object>();
foreach (DataColumn currCol in dt.Rows[0].Table.Columns)
dct.Add(currCol.ColumnName, dt.Rows[0][currCol.ColumnName].ToString());

returnDict.Add("Data", dct);

string sResponse = json.Serialize(returnDict);
byte[] byResponse = Encoding.UTF8.GetBytes(sResponse);

return new MemoryStream(byResponse);
}

Now the output looks exactly how I want it to look:

{"Status":"Success","Data":{"Key1":"Value1","Key2":"Value2","Key3":"Value3"}}

Anyway, I hope this example helps somebody else :)

JSON response from WCF escapes name values

The DataContractJsonSerializer used by default in WCF uses a XML-to-JSON mapping which causes some issues, like the one you see in ISerializable types. You can, however, use a custom formatter to change how the response will be serialized. In the example below, I'm using JSON.NET which deals with ISerializable objects "correctly".

public class StackOverflow_16674152
{
[Serializable]
public class JsonDictionary : ISerializable
{
private Dictionary<string, object> _Dictionary;
public JsonDictionary()
{
_Dictionary = new Dictionary<string, object>();
}
public JsonDictionary(SerializationInfo info, StreamingContext context)
{
_Dictionary = new Dictionary<string, object>();
SerializationInfoEnumerator enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
_Dictionary.Add(enumerator.Name, enumerator.Value);
}
}
public object this[string key]
{
get { return _Dictionary[key]; }
set { _Dictionary[key] = value; }
}
public void Add(string key, object value)
{
_Dictionary.Add(key, value);
}
public bool ContainsKey(string key)
{
return _Dictionary.ContainsKey(key);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (string key in _Dictionary.Keys)
info.AddValue(key, _Dictionary[key], _Dictionary[key] == null ? typeof(object) : _Dictionary[key].GetType());
}
}
[ServiceContract]
public class Service
{
[WebGet(UriTemplate = "", ResponseFormat = WebMessageFormat.Json)]
[MyISerializableResponseJsonBehavior]
public JsonDictionary GetCollection()
{
JsonDictionary dict = new JsonDictionary();

dict.Add("Hello world", 100);
return dict;
}
}
public class MyFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter original;
string replyAction;
public MyFormatter(IDispatchMessageFormatter original, string replyAction)
{
this.original = original;
this.replyAction = replyAction;
}

public void DeserializeRequest(Message message, object[] parameters)
{
this.original.DeserializeRequest(message, parameters);
}

public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
ISerializable serializable = result as ISerializable;
if (serializable != null)
{
string json = JsonConvert.SerializeObject(serializable);
byte[] bytes = Encoding.UTF8.GetBytes(json);
var writer = new MyRawWriter(bytes);
Message reply = Message.CreateMessage(messageVersion, replyAction, writer);
reply.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return reply;
}
else
{
return this.original.SerializeReply(messageVersion, parameters, result);
}
}

class MyRawWriter : BodyWriter
{
byte[] data;
public MyRawWriter(byte[] data)
: base(true)
{
this.data = data;
}

protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Binary");
writer.WriteBase64(data, 0, data.Length);
writer.WriteEndElement();
}
}
}
public class MyISerializableResponseJsonBehaviorAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}

public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}

public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
if (operationDescription.Messages.Count > 1)
{
dispatchOperation.Formatter = new MyFormatter(dispatchOperation.Formatter, operationDescription.Messages[1].Action);
}
}

public void Validate(OperationDescription operationDescription)
{
if (operationDescription.Messages.Count > 1)
{
var respMessage = operationDescription.Messages[1];
if (respMessage.Body.Parts.Count > 0)
{
throw new InvalidOperationException("Cannot be used with out/ref parameters");
}
}

var wga = operationDescription.Behaviors.Find<WebGetAttribute>();
var wia = operationDescription.Behaviors.Find<WebInvokeAttribute>();
WebMessageBodyStyle bodyStyle = WebMessageBodyStyle.Bare; // default
if (wga != null && wga.IsBodyStyleSetExplicitly) {
bodyStyle = wga.BodyStyle;
}

if (wia != null && wia.IsBodyStyleSetExplicitly) {
bodyStyle = wia.BodyStyle;
}

if (bodyStyle == WebMessageBodyStyle.Wrapped || bodyStyle == WebMessageBodyStyle.WrappedResponse)
{
throw new InvalidOperationException("This behavior can only be used with bare response style");
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");

WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress + "/"));
}
}

WCF REST service dictionary serialization

I've solved it by using custom made dictionary as explained here.

Since I've created this type for serialization

AjaxDictionary<string, string[]>

I had to add

[ServiceKnownType(typeof(string[]))]

on my class that is ServiceContract for returning responses.

Strange results serializing a dictionary to JSON

This is because in JSON (or JavaScript) the key name (or property in JavaScript) cannot start with a numeric value, hence it put those character after serializing them to make sure they are correct JSON format



Related Topics



Leave a reply



Submit