如果已在“[JsonProperty]”中定义,我可以通过其 属性 名称获得 属性 吗?

Can I get a property by its property name, if this is already defined in "[JsonProperty]"?

我正在开发用于 JSON 序列化的 C# 程序。

我的对象如下所示(属性 的示例):

public class Device
{
    [JsonProperty("ALLTYPES_NAME")]
    public string ALLTYPES_NAME { get; set; }
    [JsonProperty("INFORMATION")]
    public string INFORMATION { get; set; }
    ...

现在我有以下信息(在文本文件中):

ALLTYPES_NAME  "Object1"
ALLTYPES_NAME  "Object2"
INFORMATION    "Inside_Info"

我想创建两个对象,JSON 序列化如下:

"desired_objects": [
{
  "ALLTYPES_NAME": "Object1",
},
{
  "ALLTYPES_NAME": "Object2",
  "INFORMATION": "Inside_Info,
  ...

为了完成这项工作,我需要类似的东西:

temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object1";
desired_objects.Add(temp_obj);
...
temp_obj.GetPropertyByName("ALLTYPES_NAME") = "Object2";
temp_obj.GetPropertyByName("INFORMATION")   = "Inside_Information";
...

一种方法是使用模板。但是我想知道是否需要这样做,看到使用 [JsonProperty] 指令可以检索所需信息的事实,因此我的问题是:

是否存在基于 [JsonProperty ...] 的方法 .GetPropertyByName()? (或者甚至更大,[JsonProperty] 指令可以用于其他东西而不是 JSON 序列化程序吗?)

您可以将 Json.NET 的 IContractResolver to get a JsonObjectContract 用于您的 Device 对象,该对象包含有关该类型所有属性的信息,包括它们的 JSON 名称和 get/set 方法。以下扩展方法可以解决问题:

public static partial class JsonExtensions
{
    static readonly IContractResolver defaultResolver = JsonSerializer.CreateDefault().ContractResolver;

    public static void SetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
    {
        if (!TrySetJsonProperty(obj, jsonName, value, exact, resolver))
            throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
    }

    public static bool TrySetJsonProperty<T>(T obj, string jsonName, object value, bool exact = false, IContractResolver resolver = null)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
        resolver = resolver ?? defaultResolver;
        var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
        if (contract == null)
            return false;
        var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
        if (property == null || !property.Writable)
            return false;
        property.ValueProvider.SetValue(obj, value);
        return true;
    }
}

此外,如果您正在从文本文件中读取数据并且您的某些 属性 值类型不是字符串,则您需要将文件中的文本值转换为适当的 .Net 类型。以下是这样做的:

public static partial class JsonExtensions
{
    public static void SetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null) where TConvertible : IConvertible
    {
        if (!TrySetConvertibleJsonProperty(obj, jsonName, value, exact, resolver))
            throw new ArgumentException(string.Format("Could not set property {0} in {1}.", jsonName, obj));
    }

    public static bool TrySetConvertibleJsonProperty<T, TConvertible>(T obj, string jsonName, TConvertible value, bool exact = false, IContractResolver resolver = null)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));
        resolver = resolver ?? defaultResolver;
        var contract = resolver.ResolveContract(obj.GetType()) as JsonObjectContract;
        if (contract == null)
            return false;
        var property = contract.Properties.GetProperty(jsonName, exact ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
        if (property == null || !property.Writable)
            return false;
        var finalValue = value == null ? null : Convert.ChangeType(value, Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType, CultureInfo.InvariantCulture);
        property.ValueProvider.SetValue(obj, finalValue);
        return true;
    }
}

要使用它,请执行以下操作:

JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object1");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "ALLTYPES_NAME", "Object2");
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "INFORMATION", "Inside_Information");

// The following sets the following property
//    [JsonProperty("DECIMAL_DATA")] 
//    public decimal DecimalInformation { get; set; }
JsonExtensions.SetConvertibleJsonProperty(temp_obj, "DECIMAL_DATA", "3.1111");  

备注:

  • 如果使用驼峰式大小写进行序列化和反序列化,请为 IContractResolver resolver 参数传递 CamelCasePropertyNamesContractResolver

  • 该方法不适用于动态对象或字典。

  • 有关通过 JSON 属性 名称获取 属性 值的等效方法,请参阅 to

演示 fiddle here.

经过一些实验,根据 Jon Skeet 的评论,我意识到我什至不需要为此使用 JSON 指令,正如您从以下代码摘录中看到的那样:

System.Reflection.PropertyInfo[] list_of_attributes = 
  (new Own_Class()).GetType().GetProperties();
string[] list_of_attribute_names = new string[list_of_attributes.Length];
for (int i = 0; i< list_of_attributes.Length; i++)
  list_of_attribute_names[i] = list_of_attributes[i].Name;
combobox_on_form.Items.AddRange(list_of_attribute_names);