Как управлять сериализацией некоторых типов с помощью DataContractJsonSerializer - PullRequest
0 голосов
/ 14 февраля 2020

Я пытаюсь сериализовать и сохранить как 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.

...