问题
I have an object tree that I'm serializing to JSON with DataContractJsonSerializer
. Dictionary<TKey, TValue>
gets serialized but I don't like the markup - the items are not rendered like this:
{key1:value, key2:value2}
but rather like an array of serialized KeyValuePair<TKey, TValue>
objects:
[{
"__type":"KeyValuePairOfstringanyType:#System.Collections.Generic",
"key":"key1",
"value":"value1"
},
{
"__type":"KeyValuePairOfstringanyType:#System.Collections.Generic",
"key":"key2",
"value":"value2"
}]
Ugly, isn't it?
So, I avoid this by wrapping the generic Dictionary in a custom object that implements ISerializable
, and I implement my custom serialization in the GetObjectData
method (and it takes just 3 lines).
Now the problem - I can't make my class derive from Dictionary<TKey, TValue>
, so I implement all logic (Add
, Clear
, etc.) in my custom class, being applied to a private Dictionary<TKey, TValue>
field. Inheritance would be preferable as I'll have all generic Dictionary functionality at my disposal when using my custom object.
The problem with inheritance is that Dictionary<TKey, TValue>
implements ISerializable
on its own, and DataContractJsonSerializer
seems to prefer this implementation even if I implement ISerializable
explicitly from my custom class, like this:
public class MyClass : Dictionary<string, object>, ISerializable
{
public override void GetObjectData(SerializationInfo info,
StreamingContext context)
}
I was actually surprised that this is possible as it allows me to implement the same interface twice without being obviously able to use explicit interface implementation - so I analyzed the situation in more detail in a blog post about multiple interface implementation
So, according to the experiments I did there, the serializer should be calling my implementation of ISerializable, no matter what type of casting is used internally -
((ISerializable)((Dictionary<,>)obj)).GetObjectData(...)
or:
((ISerializable)obj).GetObjectData(...)
but it apparently isn't happening as I see in the resulting JSON that the KeyValuePair<TKey, TValue>
serializer still being called. What might be happening that I'm missing?
Update: The answers and comments I'm getting so far are pretty much only suggesting workarounds. I noted, however, that I have a workaround that works quite well so with asking this question I have 2 objectives:
Eventually make it work with the original design - and I'm not going to change serialization logic just for that, there is a lot of code and logic dependent on it
To unserstand the mystery of why isn't the
DataContractJsonSerializer
using my serialization code - as can be seen in the blog post I referred, I have made all kinds of experiments with interface implementation and inheritance and I was confident that I'm grasping all the ins and outs of the process, so I'm troubled by failing to understand what's hapenning in this case
回答1:
One option is using a surrogate property and have the dictionary be inside the custom ISerializable type, that way you don't need to worry about inheritance:
public Dictionary<string, string> NodeData { get; set; }
[DataMember(Name="NodeData")]
private CustomDictionarySerializer NodeDataSurrogate
{
get
{
return new CustomDictionarySerializer(NodeData);
}
set
{
NodeData = value._data;
}
}
[Serializable]
private class CustomDictionarySerializer : ISerializable
{
public Dictionary<string, string> _data;
public CustomDictionarySerializer(Dictionary<string, string> dict)
{
_data = dict;
}
public CustomDictionarySerializer(SerializationInfo info, StreamingContext context)
{
_data = new Dictionary<string, string>();
var valueEnum = info.GetEnumerator();
while(valueEnum.MoveNext())
{
_data[valueEnum.Current.Name] = valueEnum.Current.Value.ToString();
}
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
foreach (var pair in _data)
{
info.AddValue(pair.Key, pair.Value);
}
}
}
回答2:
Seems, there is no way to customize DataContractJsonSerializer.
If you still want to achieve what you want, consider using Json.Net. It's faster and more flexible than DataContractJsonSerializer. Look at JsonConverter conception of Json.Net. It gives you the possibility to customize serialization/deserialization process.
Moreover default implementation of dictionary serialization is exact as you want http://james.newtonking.com/projects/json/help/SerializingCollections.html.
回答3:
As @Pashec mentioned, if you use Json.NET, you can try the following:
public Message <methodname> {
...
string jsonMessage = JsonConvert.SerializeObject(myObjectTree);
return WebOperationContext.Current.CreateTextResponse(jsonMessage, "application/javascript; charset=utf-8", Encoding.UTF8);
}
来源:https://stackoverflow.com/questions/7498208/serialize-dictionarytkey-tvalue-to-json-with-datacontractjsonserializer