Serialize Dictionary<TKey, TValue> to JSON with DataContractJsonSerializer

廉价感情. 提交于 2019-12-18 18:55:43

问题


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:

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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!