Я пытаюсь сериализовать и сохранить как JSON настройки моего приложения, и у меня возникают некоторые проблемы с сериализацией некоторых типов.
Настройки загружаются, когда приложение запускается в ResourceDictionary
, который добавляется в Application.Current.Resources.MergedDictionaries
, поскольку это приложение WPF
.
Когда приложение закрывается, я должен получить это ResourceDictionary
, сериализовать в JSON и записать на диск.
В настоящее время я сталкиваюсь с тремя проблемами:
1) Double
свойства, которые не имеют десятичного значения, сериализуются и десериализуются как Int32
, что вызывает некоторые проблемы при попытке приложения разыграть Object
до Double
. Разве нет возможности заставить сериализацию двойным?
public double StartupTop
{
get => (double)GetValue();
set => SetValue(value);
}
2) FontFamily
свойства странные. Вместо экспорта только имени шрифта экспортируется все семейство шрифтов (light, itali c, et c).
3) Я также получаю исключение при попытке десериализации json:
Type 'System.Windows.Markup.XmlLanguage' cannot be serialized.
public enum AppTheme
{
Light,
Medium,
Dark,
VeryDark,
}
Рабочий случай:
var json = "";
var dic = new ResourceDictionary
{
{"EnumProperty", AppTheme.Dark},
{"IntProperty", 42},
{"DoubleProperty", 666D},
{"FontFamilyProperty", new FontFamily("Segoe UI")},
{"ColorProperty", Colors.Blue},
{"RectProperty", new Rect(10, 20, 30, 40)},
{"ArrayListProperty", new ArrayList
{
1, 2, 3, 4, 5
}},
};
//Save settings.
using (var ms = new MemoryStream())
{
var settings = new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true,
EmitTypeInformation = EmitTypeInformation.Always,
DataContractSurrogate = new SettingsSurrogate(), //Use the Surrogate only during serialization.
KnownTypes = new List<Type>
{
typeof(AppTheme),
typeof(ArrayList),
typeof(Color),
typeof(FontFamily),
typeof(Rect),
}
};
var ser = new DataContractJsonSerializer(typeof(Dictionary<string, object>), settings);
ser.WriteObject(ms, dic.Keys.Cast<string>().ToDictionary(x => x, x => dic[x]));
json = Encoding.UTF8.GetString(ms.ToArray());
//File.WriteAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Test.json"), json);
}
//Load settings.
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var settings = new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true,
EmitTypeInformation = EmitTypeInformation.Always,
KnownTypes = new List<Type>
{
typeof(AppTheme),
typeof(ArrayList),
typeof(Color),
typeof(FontFamily),
typeof(Rect),
}
};
var ser = new DataContractJsonSerializer(typeof(Dictionary<string, object>), settings);
var list = ser.ReadObject(ms) as Dictionary<string, object>;
//Ignore empty settings.
if (list == null || list.Count == 0)
return;
foreach (var item in list)
{
if (dic[item.Key] != item.Value || dic[item.Key].GetType() != item.Value.GetType())
throw new Exception("Wrong value");
}
}
Суррогатное
internal class SettingsSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(FontFamily))
return ((FontFamily) obj).Source;
if (obj is Color color)
return $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}";
if (obj is Rect rect)
return $"#{rect.Left};{rect.Top};{rect.Width};{rect.Height}";
//Convert value to string, using whatever format I need.
//Right now I'm following the XAML set of formats for the types.
return obj;
}
//Other members go here, but they are not necessary for me.
}
I Я использую оба типа несколько раз в разных свойствах. Есть ли способ контролировать сериализацию типов?
EDIT:
Сейчас я использую суррогат для преобразования некоторых значений в строку перед сериализацией. Но это оставляет мне задачу вручную конвертировать обратно каждое свойство при десериализации, поскольку для меня нет Type
.