JSON 序列化属性扩展对象并直接序列化其字段

JSON Serialization attribute to expand object and serialize its fields directly

我有一些 class 具有以下结构

public class A {
  [JsonProperty(PropertyName = "prop_b")]
  public B PropB {get; set;}
}

public class B {
  [JsonProperty(PropertyName = "val1")]
  public int Val1 {get; set;}

  [JsonProperty(PropertyName = "val2")]
  public int Val2 {get; set;}
}

这将被序列化为以下方案的 JSON:

{
  "prop_b": { "val1": X, "val2": Y }
}

有没有办法跳过B直接序列化字段,而不改变class结构?我会假设会有一些属性,或者我可以实现一个。

{
   "val1": X,
   "val2": Y
}

一种可能是创建 JsonConverter。

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
  public sealed class ImportChildAttribute : Attribute
  {
  }

  class ImportChildJsonConverter : JsonConverter
  {
    public override bool CanConvert(Type objectType)
    {
      var attr = CustomAttributeExtensions.GetCustomAttribute(objectType.GetTypeInfo(), typeof(ImportChildAttribute), true);
      if (attr != null)
      {
        var props = objectType.GetProperties();
        if (props.Length != 1)
          throw new NotSupportedException($"Only supports {nameof(ImportChildAttribute)} on classes with one property.");
        return true;
      }

      return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
      // Deserialize the object into the first property.
      var props = objectType.GetProperties();
      var obj = Activator.CreateInstance(objectType);
      var val = serializer.Deserialize(reader, props[0].PropertyType);
      props[0].SetValue(obj, val);
      return obj;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
      // Find the only property and serialize it.
      var props = value.GetType().GetProperties();
      serializer.Serialize(writer, props[0].GetValue(value));
    }
  }

然后你可以把 ImportChild 属性放在你想要的所有 类 上。

  [ImportChild]
  public class A
  {
    [JsonProperty(PropertyName = "prop_b")]
    public B PropB { get; set; }
  }

  public class B
  {
    [JsonProperty(PropertyName = "val1")]
    public int Val1 { get; set; }

    [JsonProperty(PropertyName = "val2")]
    public int Val2 { get; set; }
  }

终于来试试看:

  var settings = new JsonSerializerSettings
  {
    Converters = new[] { new ImportChildJsonConverter() },
    Formatting = Formatting.Indented
  };

  var obj = new A { PropB = new B { Val1 = 1, Val2 = 2 } };
  string json = JsonConvert.SerializeObject(obj, settings);
  Console.WriteLine(json);
  /* Outputs:
     {
       "val1": 1,
       "val2": 2
     }
  */

  var originalObj = JsonConvert.DeserializeObject<A>(json, settings);
  // originalObj and obj are now identical.