Сериализуйте словарь <TKey, TValue> в JSON с помощью DataContractJsonSerializer - PullRequest
15 голосов
/ 21 сентября 2011

У меня есть дерево объектов, которое я сериализую в 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> все еще вызывается.Что может случиться, что я пропускаю?

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

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

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

Ответы [ 3 ]

4 голосов
/ 18 сентября 2013

Один из вариантов - использование суррогатного свойства, при котором словарь должен находиться внутри пользовательского типа 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);
        }
    }
}
1 голос
/ 10 октября 2011

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

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

Более того, стандартная реализация сериализации словаря точна, как вы хотите http://james.newtonking.com/projects/json/help/SerializingCollections.html.

0 голосов
/ 25 февраля 2012

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

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