Сериализуйте словарь<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
используя мой код сериализации - как видно из поста в блоге, который я упоминал, я провел все виды экспериментов с реализацией и наследованием интерфейса, и я был уверен, что понимаю все тонкости процесса, поэтому я обеспокоены неспособностью понять, что происходит в этом случае
3 ответа
Одним из вариантов является использование суррогатного свойства, и словарь должен быть внутри пользовательского типа 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);
}
}
}
Кажется, нет способа настроить DataContractJsonSerializer.
Если вы все еще хотите достичь того, чего хотите, рассмотрите возможность использования Json.Net. Это быстрее и гибче, чем DataContractJsonSerializer. Посмотрите на концепцию JsonConverter для Json.Net. Это дает вам возможность настроить процесс сериализации / десериализации.
Более того, стандартная реализация сериализации словаря является точной, как вы хотите http://james.newtonking.com/projects/json/help/SerializingCollections.html.
Как упомянул @Pashec, если вы используете Json.NET, вы можете попробовать следующее:
public Message <methodname> {
...
string jsonMessage = JsonConvert.SerializeObject(myObjectTree);
return WebOperationContext.Current.CreateTextResponse(jsonMessage, "application/javascript; charset=utf-8", Encoding.UTF8);
}