无法从Json

时间:2019-03-01 13:45:37

标签: c# json.net

我有以下基于Json的配置文件:

{
  "PostProcessing": {
    "ValidationHandlerConfiguration": {
      "MinimumTrustLevel": 80,
      "MinimumMatchingTrustLevel": 75
    },
    "MatchingCharacterRemovals": [
      "-",
      "''",
      ":"
    ]
  },
  "Processing": {
    "OrderSelection": {
      "SelectionDaysInterval": 30,
      "SelectionDaysMaximum": 365
    }
  }
}

作为序列化框架,我使用 Newtonsoft 。要将这个配置序列化为对象,我实现了以下类:

[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
    [JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
    public PostRecognitionConfiguration PostRecognitionConfiguration { get; set; }

    [JsonProperty(PropertyName = "Processing", Required = Required.Always)]
    public ProcessRecognitionConfiguration ProcessRecognitionConfiguration { get; set; }
}

[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
    [JsonProperty(Required = Required.Always)]
    public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }

    [JsonProperty] public List<string> MatchingCharacterRemovals { get; set; }
}

[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
    [JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
    public OrderSelectionConfiguration OrderSelectionConfiguration { get; set; }
}

在一个类中,我尝试使用 IConfigurationSection.Get()将特定的配置节序列化为这些类结构。

var serializedConfiguration = this.ConfigurationSection.Get<RecognitionConfiguration>();

但是当我调试代码时,我总是得到一个“空”变量 serializedConfiguration ,它不是null,但是所有属性都是null。

enter image description here

如果我使用

this.ConfigurationSection.GetSection("Processing").Get<ProcessRecognitionConfiguration>()

或更改json文件中属性的命名以完全匹配此类中的属性名称:

{   
  "ProcessRecognitionConfiguration": {
    "OrderSelectionConfiguration": {
      "SelectionDaysInterval": 30,
      "SelectionDaysMaximum": 365
    }
  }
}

它工作正常。您有什么想法,为什么在 JsonProperty 上设置 PropertyName 似乎没有效果?

3 个答案:

答案 0 :(得分:2)

那是设计使然。通过配置绑定到POCO是按照惯例完成的。不像模型绑定到Controller Action参数。

它将POCO上的属性名称与提供的JSON中的键匹配。

引用Configuration in ASP.NET Core

因此,您可以更改设置以匹配原始问题中显示的类,或者更改该类以匹配基于Json的配置文件中的设置键。

[JsonObject(MemberSerialization.OptIn)]
public class RecognitionConfiguration {
    [JsonProperty(PropertyName = "PostProcessing", Required = Required.Always)]
    public PostRecognitionConfiguration PostProcessing{ get; set; }

    [JsonProperty(PropertyName = "Processing", Required = Required.Always)]
    public ProcessRecognitionConfiguration Processing{ get; set; }
}

[JsonObject(MemberSerialization.OptIn)]
public class PostRecognitionConfiguration {
    [JsonProperty(Required = Required.Always)]
    public ValidationHandlerConfiguration ValidationHandlerConfiguration { get; set; }

    [JsonProperty] 
    public List<string> MatchingCharacterRemovals { get; set; }
}

[JsonObject(MemberSerialization.OptIn)]
public class ProcessRecognitionConfiguration {
    [JsonProperty(PropertyName = "OrderSelection", Required = Required.Always)]
    public OrderSelectionConfiguration OrderSelection { get; set; }
}

public partial class ValidationHandlerConfiguration {
    [JsonProperty("MinimumTrustLevel")]
    public long MinimumTrustLevel { get; set; }

    [JsonProperty("MinimumMatchingTrustLevel")]
    public long MinimumMatchingTrustLevel { get; set; }
}


public partial class OrderSelectionConfiguration {
    [JsonProperty("SelectionDaysInterval")]
    public long SelectionDaysInterval { get; set; }

    [JsonProperty("SelectionDaysMaximum")]
    public long SelectionDaysMaximum { get; set; }
}

答案 1 :(得分:1)

编辑:我发现这个解决方案比以前的解决方案要愉快得多:将所有内容绑定到ExpandoObject中,将它们写入JSON并使用JSON.NET将其绑定回去。 Using the code of this article

namespace Microsoft.Extensions.Configuration
{

    public static class ConfigurationBinder
    {

        public static void BindJsonNet(this IConfiguration config, object instance)
        {
            var obj = BindToExpandoObject(config);

            var jsonText = JsonConvert.SerializeObject(obj);
            JsonConvert.PopulateObject(jsonText, instance);
        }

        private static ExpandoObject BindToExpandoObject(IConfiguration config)
        {
            var result = new ExpandoObject();

            // retrieve all keys from your settings
            var configs = config.AsEnumerable();
            foreach (var kvp in configs)
            {
                var parent = result as IDictionary<string, object>;
                var path = kvp.Key.Split(':');

                // create or retrieve the hierarchy (keep last path item for later)
                var i = 0;
                for (i = 0; i < path.Length - 1; i++)
                {
                    if (!parent.ContainsKey(path[i]))
                    {
                        parent.Add(path[i], new ExpandoObject());
                    }

                    parent = parent[path[i]] as IDictionary<string, object>;
                }

                if (kvp.Value == null)
                    continue;

                // add the value to the parent
                // note: in case of an array, key will be an integer and will be dealt with later
                var key = path[i];
                parent.Add(key, kvp.Value);
            }

            // at this stage, all arrays are seen as dictionaries with integer keys
            ReplaceWithArray(null, null, result);

            return result;
        }

        private static void ReplaceWithArray(ExpandoObject parent, string key, ExpandoObject input)
        {
            if (input == null)
                return;

            var dict = input as IDictionary<string, object>;
            var keys = dict.Keys.ToArray();

            // it's an array if all keys are integers
            if (keys.All(k => int.TryParse(k, out var dummy)))
            {
                var array = new object[keys.Length];
                foreach (var kvp in dict)
                {
                    array[int.Parse(kvp.Key)] = kvp.Value;
                }

                var parentDict = parent as IDictionary<string, object>;
                parentDict.Remove(key);
                parentDict.Add(key, array);
            }
            else
            {
                foreach (var childKey in dict.Keys.ToList())
                {
                    ReplaceWithArray(input, childKey, dict[childKey] as ExpandoObject);
                }
            }
        }

    }
}

用法:

        var settings = new MySettings();
        this.Configuration.BindJsonNet(settings);

这是我的测试MySettings课:

public class MySettings
{

    [JsonProperty("PostProcessing")]
    public SomeNameElseSettings SomenameElse { get; set; }

    public class SomeNameElseSettings
    {

        [JsonProperty("ValidationHandlerConfiguration")]
        public ValidationHandlerConfigurationSettings WhateverNameYouWant { get; set; }

        public class ValidationHandlerConfigurationSettings
        {

            [JsonProperty("MinimumTrustLevel")]
            public int MinimumTrustLevelFoo { get; set; }

            [JsonProperty("MinimumMatchingTrustLevel")]
            public int MinimumMatchingTrustLevelBar { get; set; }
        }
    }
}

打完电话,我随心所欲:

enter image description here


旧答案:

根据source code here,根本(几乎)不可能做您需要的事情。我已经尝试过JsonPropertyDataContract,但都没有受到活页夹的认可,仅仅是因为源代码本身只是使用属性名。

如果您仍然坚持,则有两种可能性,但是我不建议任何一种,因为更改属性的名称要简单得多:

  • 在此处分叉您的源代码,或简单地复制该文件(在我试图跟踪代码的过程中,我将所有方法重命名为Bind2BindInstance2等),然后重写相应的代码。

  • 此代码非常特定于当前实现,因此不符合未来发展趋势:当前代码正在调用config.GetSection(property.Name),因此您可以编写自己的IConfiguration并为其提供名称GetSection方法,然后将其点击进入引导过程,而不使用默认方法。

答案 2 :(得分:-1)

PropertyName更改为JsonProperty确实有效。这是我尝试过的方法,对我确实有用:

我的JSON数据:

{"name": "John","age": 30,"cars": [ "Ford", "BMW", "Fiat" ]}

和型号:

public class RootObject
    {
        [JsonProperty(PropertyName ="name")]
        public string Apple { get; set; }
        public int age { get; set; }
        public List<string> cars { get; set; }
    }

这是代码:

RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);

这是我得到的输出

enter image description here

您需要在PropertyName中将JsonProperty设置为与json文件属性名称相同,但是您的C#模型属性可以是您想要的,只是它们需要用[JsonProperty(PropertyName ="jsonPropertyName")]装饰这可以帮助您解决问题。

快乐的编码...