人口普及使用自定义iDefaultContractResolver从Json合并对象集合会导致数据移位/损坏

时间:2016-01-17 15:15:22

标签: c# json json.net

这是我正在序列化/反序列化的类数据结构:

(JobSchedulerService.java:118)
        at com.google.common.util.concurrent.AbstractScheduledService$1$1.run(Ab
stractScheduledService.java:174)
        at com.google.common.util.concurrent.Callables$3.run(Callables.java:95)
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
        at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.
access$301(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.
run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: InvalidRequestException(why:Keyspace 'Usergrid_Applications' does not
 exist)
        at org.apache.cassandra.thrift.Cassandra$set_keyspace_result.read(Cassan
dra.java:5540)
        at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:78)
        at org.apache.cassandra.thrift.Cassandra$Client.recv_set_keyspace(Cassan
dra.java:540)
        at org.apache.cassandra.thrift.Cassandra$Client.set_keyspace(Cassandra.j
ava:527)
        at me.prettyprint.cassandra.connection.client.HThriftClient.getCassandra
(HThriftClient.java:110)

我使用以下内容确保只有设置名称和值存储在JSON中,其余的都在应用程序本身内构建,不需要通过JSON保存/加载;

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>();
    }
}

自定义iDefaultContractResolver是我在SE周围浮动的一个,但为了完整性将包含在这里:

    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 { }
    }

它只是像我期望的那样只保存我要保存的属性,但加载导致数据被移动&#34;,所有设置值都将加载到错误的设置等等并尝试访问它们时导致类型不匹配。我已尝试在加载部分中使用和不使用自定义Json设置,但行为没有区别。另外值得注意的是,如果没有iDefaultContractResolver,整个结构可以保存/加载就好了,但是我的JSON文件不会混杂不需要的数据。

以下是正在构建并使用示例数据填充的设置类:

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来源和结果:

完成具有设置信息和默认值的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);

我的saveSettings扩展程序保存的数据,旨在仅保存所需的信息,例如组名子项和设置名称和值(这看起来是正确的)

{
  "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": []
}

但是当加载并与现有类对象合并时,这就是结果数据的样子,请注意控件类型中的重复项和无效值导致类型不匹配IE无法将{ "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": [] } 值加载到{{ 1}}由复选框使用。

long

1 个答案:

答案 0 :(得分:1)

您应该可以KeyedIListMergeConverter使用KeyedListMergeConverter List<T>(而不是专门用于合并[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}}