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.
我有一些 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.