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

Сериализовать словарь <TKey, TValue> в JSON с помощью DataContractJsonSerializer

У меня есть дерево объектов, которое я сериализую в JSON с DataContractJsonSerializer. Dictionary<TKey, TValue> сериализуется, но мне не нравится разметка - элементы не отображаются следующим образом:

{key1:value, key2:value2}

а скорее как массив сериализованных KeyValuePair<TKey, TValue> объектов:

[{
    "__type":"KeyValuePairOfstringanyType:#System.Collections.Generic",
    "key":"key1",
    "value":"value1"
 },     
 {
    "__type":"KeyValuePairOfstringanyType:#System.Collections.Generic",
    "key":"key2",
    "value":"value2"
 }]

Ужасно, не правда ли?

Итак, я избегаю этого, обертывая общий словарь в пользовательский объект, реализующий ISerializable, и реализую свою собственную сериализацию в методе GetObjectData (и занимает всего 3 строки).

Теперь проблема - я не могу сделать свой класс из Dictionary<TKey, TValue>, поэтому я реализую всю логику (Add, Clear и т.д.) в своем пользовательском классе, применяя к закрытому Dictionary<TKey, TValue> поле. Наследование было бы предпочтительнее, поскольку у меня будут все общие функции словаря в моем распоряжении при использовании моего пользовательского объекта.

Проблема с наследованием заключается в том, что Dictionary<TKey, TValue> реализует ISerializable самостоятельно, а DataContractJsonSerializer, похоже, предпочитает эту реализацию, даже если я реализую ISerializable явно из моего пользовательского класса, например:

public class MyClass : Dictionary<string, object>, ISerializable
{
    public override void GetObjectData(SerializationInfo info, 
        StreamingContext context)
}

Я был действительно удивлен, что это возможно, поскольку это позволяет мне реализовать один и тот же интерфейс дважды, не имея явно возможности использовать явную реализацию интерфейса, поэтому я более подробно проанализировал ситуацию в сообщении в блоге о реализации нескольких интерфейсов

Итак, согласно проведенным там экспериментам, сериализатор должен называть мою реализацию ISerializable, независимо от того, какой тип литья используется внутри -

((ISerializable)((Dictionary<,>)obj)).GetObjectData(...)

или

((ISerializable)obj).GetObjectData(...)

но это, по-видимому, не происходит, как я вижу в полученном JSON, который все еще вызывает сериализатор KeyValuePair<TKey, TValue>. Что может случиться, что мне не хватает?

Обновление: Ответы и комментарии, которые я получаю до сих пор, в значительной степени только предполагают обходные пути. Я отметил, однако, что у меня есть обходное решение, которое работает очень хорошо, поэтому, задав этот вопрос, у меня есть две цели:

  • В конце концов заставьте его работать с оригинальным дизайном - и я не собираюсь менять логику сериализации только для этого, в нем много кода и логики.

  • Чтобы не понимать тайну, почему не является DataContractJsonSerializer с использованием моего кода сериализации - как видно из сообщения в блоге, которое я упоминал, я сделал всевозможные эксперименты с реализацией интерфейса и наследованием, и я был уверенный, что я улавливаю все входы и выходы процесса, поэтому я обеспокоен тем, что не понял, что происходит в этом случае

4b9b3361

Ответ 1

Один из вариантов использует свойство суррогата и имеет словарь внутри пользовательского типа ISerializable, поэтому вам не нужно беспокоиться о наследовании:

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

Кажется, нет способа настроить DataContractJsonSerializer.

Если вы все еще хотите достичь того, чего хотите, рассмотрите возможность использования Json.Net. Это быстрее и гибче, чем DataContractJsonSerializer. Посмотрите на концепцию JsonConverter Json.Net. Это дает вам возможность настроить процесс сериализации/десериализации.

Кроме того, по умолчанию реализация словарной сериализации является точной, как вы хотите http://james.newtonking.com/projects/json/help/SerializingCollections.html.

Ответ 3

Как упоминалось в @Pashec, если вы используете Json.NET, вы можете попробовать следующее:

public Message <methodname> {
    ...
    string jsonMessage = JsonConvert.SerializeObject(myObjectTree);
    return WebOperationContext.Current.CreateTextResponse(jsonMessage, "application/javascript; charset=utf-8", Encoding.UTF8);
}