Populating and merging a collection of objects with Json using a custom iDefaultContractResolver results in shifted / corrupted data

This is my class data structure that I serialize / deserialize:

public class SettingGroup
{
    public string name { get; set; }
    public string description { get; set; }
    public bool visible { get; set; }
    public ObservableCollection<SettingGroup> groups { get; set; }
    public ObservableCollection<Setting> settings { get; set; }
    public SettingGroup()
    {
        groups = new ObservableCollection<SettingGroup>();
        settings = new ObservableCollection<Setting>();
        visible = true;
    }
}

public class Setting
{
    public string name { get; set; }
    public string description { get; set; }
    public bool visible { get; set; }
    public DescriptionVisibility descriptionVisibility { get; set; }
    public Dictionary<string, dynamic> configuration { get; set; }
    public dynamic settingValue { get; set; }
    public SettingType settingType { get; set; }
    public SettingControl settingControl { get; set; }
    public Setting()
    {
        visible = true;
        configuration = new Dictionary<string, dynamic>();
    }
}

I use the following to ensure that only names and parameter values ​​are stored in JSON, the rest are structured inside the application itself and do not need to be saved / loaded via JSON;

    private static string safeFileName(string fileName)
    {
        string regexSearch = new string(Path.GetInvalidFileNameChars()) + " ";
        Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
        fileName = r.Replace(fileName, "");
        return fileName;
    }
    public static void saveSettings(this SettingGroup settingGroup, string fileName = "")
    {
        var jsonResolver = new IgnorableSerializerContractResolver();

        jsonResolver.Ignore(typeof(SettingGroup), "visible");
        jsonResolver.Ignore(typeof(SettingGroup), "description");
        jsonResolver.Ignore(typeof(Setting), "visible");
        jsonResolver.Ignore(typeof(Setting), "descriptionVisibility");
        jsonResolver.Ignore(typeof(Setting), "configuration");
        jsonResolver.Ignore(typeof(Setting), "settingType");
        jsonResolver.Ignore(typeof(Setting), "settingControl");
        jsonResolver.Ignore(typeof(Setting), "description");


        var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };

        if (string.IsNullOrWhiteSpace(fileName))
            fileName = safeFileName(settingGroup.name);

        try
        {
            if (!Directory.Exists(Global.DataDirectory)) Directory.CreateDirectory(Global.DataDirectory);
            File.WriteAllText(Path.Combine(Global.DataDirectory, fileName+".json"), JsonConvert.SerializeObject(settingGroup, Newtonsoft.Json.Formatting.Indented, jsonSettings));
        }
        catch { }
    }
    public static void loadSettings(this SettingGroup settingGroup, string fileName = "")
    {
        var jsonResolver = new IgnorableSerializerContractResolver();

        jsonResolver.Ignore(typeof(SettingGroup), "visible");
        jsonResolver.Ignore(typeof(SettingGroup), "description");
        jsonResolver.Ignore(typeof(Setting), "visible");
        jsonResolver.Ignore(typeof(Setting), "descriptionVisibility");
        jsonResolver.Ignore(typeof(Setting), "configuration");
        jsonResolver.Ignore(typeof(Setting), "settingType");
        jsonResolver.Ignore(typeof(Setting), "settingControl");
        jsonResolver.Ignore(typeof(Setting), "description");

        if (string.IsNullOrWhiteSpace(fileName))
            fileName = safeFileName(settingGroup.name);

        try
        {
            if (!Directory.Exists(Global.DataDirectory)) Directory.CreateDirectory(Global.DataDirectory);
            var serializerSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Auto, ContractResolver = jsonResolver };
            JsonConvert.PopulateObject(File.ReadAllText(Path.Combine(Global.DataDirectory, fileName + ".json")), settingGroup, serializerSettings);
        }
        catch { }
    }

The custom iDefaultContractResolver is the one that I saw as floating around SE, but will still enable it for completeness:

public class IgnorableSerializerContractResolver : DefaultContractResolver
{
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver()
    {
        this.Ignores = new Dictionary<Type, HashSet<string>>();
    }

    public void Ignore(Type type, params string[] propertyName)
    {
        if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName)
        {
            this.Ignores[type].Add(prop);
        }
    }

    public bool IsIgnored(Type type, string propertyName)
    {
        if (!this.Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[type].Count == 0) return true;

        return this.Ignores[type].Contains(propertyName);
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        // need to check basetype as well for EF -- @per comment by user576838
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName))
        {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}

, , , , "" , .., , . Json - . , iDefaultContractResolver / , id JSON .

:

        SettingGroup Settings = new SettingGroup();
        Settings.name = "Application Settings";
        Settings.description = "Common application settings.";

        SettingGroup generalSettings = new SettingGroup();
        generalSettings.name = "General settings";
        Settings.groups.Add(generalSettings);

        SettingGroup themeSettings = new SettingGroup();
        themeSettings.name = "Theme settings";
        Settings.groups.Add(themeSettings);

        SettingGroup updateSettings = new SettingGroup();
        updateSettings.name = "Update settings";
        Settings.groups.Add(updateSettings);

        SettingGroup startupSettings = new SettingGroup();
        startupSettings.name = "Startup settings";
        generalSettings.groups.Add(startupSettings);

        Setting startWithWindows = new Setting();
        startWithWindows.name = "Start IM with Windows";
        startWithWindows.settingValue = true;
        startWithWindows.settingControl = SettingControl.Checkbox;
        startupSettings.settings.Add(startWithWindows);

        Setting startMinimized = new Setting();
        startMinimized.name = "Start IM minimized";
        startMinimized.settingValue = true;
        startMinimized.settingControl = SettingControl.Checkbox;
        startupSettings.settings.Add(startMinimized);

        SettingGroup performanceSettings = new SettingGroup();
        performanceSettings.name = "Performance settings";
        generalSettings.groups.Add(performanceSettings);

        Setting threadPriority = new Setting();
        threadPriority.name = "Thread priority";
        threadPriority.description = "This setting may not have a noticeible impact on all platforms, especially higer end ones.";
        threadPriority.settingValue = 3;
        threadPriority.settingControl = SettingControl.Slider;
        threadPriority.configuration.Add("lowVal",0);
        threadPriority.configuration.Add("highVal", 7);
        threadPriority.configuration.Add("interval", 1);
        performanceSettings.settings.Add(threadPriority);

Json:

Json, :

{
  "name": "Application Settings",
  "description": "Common application settings.",
  "visible": true,
  "groups": [
    {
      "name": "General settings",
      "description": null,
      "visible": true,
      "groups": [
        {
          "name": "Startup settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Start IM with Windows",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            },
            {
              "name": "Start IM minimized",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        },
        {
          "name": "Performance settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Thread priority",
              "description": "This setting may not have a noticeible impact on all platforms, especially higer end ones.",
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {
                "lowVal": 0,
                "highVal": 7,
                "interval": 1
              },
              "settingValue": 3,
              "settingType": 0,
              "settingControl": 2
            }
          ]
        }
      ],
      "settings": []
    },
    {
      "name": "Theme settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "Update settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "General settings",
      "description": null,
      "visible": true,
      "groups": [
        {
          "name": "Startup settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Start IM with Windows",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            },
            {
              "name": "Start IM minimized",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        },
        {
          "name": "Performance settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Thread priority",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": 3,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        }
      ],
      "settings": []
    },
    {
      "name": "Theme settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "Update settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    }
  ],
  "settings": []
}

, saveSettings, , , ( )

{
  "name": "Application Settings",
  "groups": [
    {
      "name": "General settings",
      "groups": [
        {
          "name": "Startup settings",
          "groups": [],
          "settings": [
            {
              "name": "Start IM with Windows",
              "settingValue": true
            },
            {
              "name": "Start IM minimized",
              "settingValue": true
            }
          ]
        },
        {
          "name": "Performance settings",
          "groups": [],
          "settings": [
            {
              "name": "Thread priority",
              "settingValue": 3
            }
          ]
        }
      ],
      "settings": []
    },
    {
      "name": "Theme settings",
      "groups": [],
      "settings": []
    },
    {
      "name": "Update settings",
      "groups": [],
      "settings": []
    }
  ],
  "settings": []
}

, , , . IE long bool .

{
  "name": "Application Settings",
  "description": "Common application settings.",
  "visible": true,
  "groups": [
    {
      "name": "General settings",
      "description": null,
      "visible": true,
      "groups": [
        {
          "name": "Startup settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Start IM with Windows",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            },
            {
              "name": "Start IM minimized",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        },
        {
          "name": "Performance settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Thread priority",
              "description": "This setting may not have a noticeible impact on all platforms, especially higer end ones.",
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {
                "lowVal": 0,
                "highVal": 7,
                "interval": 1
              },
              "settingValue": 3,
              "settingType": 0,
              "settingControl": 2
            }
          ]
        }
      ],
      "settings": []
    },
    {
      "name": "Theme settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "Update settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "General settings",
      "description": null,
      "visible": true,
      "groups": [
        {
          "name": "Startup settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Start IM with Windows",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            },
            {
              "name": "Start IM minimized",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": true,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        },
        {
          "name": "Performance settings",
          "description": null,
          "visible": true,
          "groups": [],
          "settings": [
            {
              "name": "Thread priority",
              "description": null,
              "visible": true,
              "descriptionVisibility": 0,
              "configuration": {},
              "settingValue": 3,
              "settingType": 0,
              "settingControl": 0
            }
          ]
        }
      ],
      "settings": []
    },
    {
      "name": "Theme settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    },
    {
      "name": "Update settings",
      "description": null,
      "visible": true,
      "groups": [],
      "settings": []
    }
  ],
  "settings": []
}
+1
1

KeyedIListMergeConverter ( KeyedListMergeConverter, List<T>) Json.Net PopulateObject - , [JsonMergeKey] :

public class SettingGroup
{
    [JsonMergeKey]
    public string name { get; set; }
    // Remainder unchanged
}

public class Setting
{
    [JsonMergeKey]
    public string name { get; set; }
    // Remainder unchanged
}

:

        if (!Directory.Exists(Global.DataDirectory))
            Directory.CreateDirectory(Global.DataDirectory);
        var serializerSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Auto, ContractResolver = jsonResolver };
        serializerSettings.Converters.Add(new KeyedIListMergeConverter(settings.ContractResolver));
        JsonConvert.PopulateObject(File.ReadAllText(Path.Combine(Global.DataDirectory, fileName + ".json")), settingGroup, serializerSettings);
+1

Source: https://habr.com/ru/post/1623046/


All Articles