我需要将平面 JSON 映射到嵌套对象,其中平面属性用Flat
属性标记。例如映射
{
"x": 1,
"y": 2,
"z": 3
}
到以下模型
public class Foo {
public int X { get; set; }
public int Y { get; set; }
}
public class Bar {
[JsonFlat]
public Foo Foo { get; set; }
public int Z { get; set; }
}
应该给Bar { Foo { X = 1, Y = 2 }, Z = 3 }
。我已经实现了将复杂对象转换为 JSON 但反之亦然:
public class FlatJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
WriteObject(writer, value, serializer);
writer.WriteEndObject();
}
private void WriteObject(JsonWriter writer, object value, JsonSerializer serializer)
{
var t = JToken.FromObject(value);
var flatNames = new List<string>();
if (t.Type == JTokenType.Object)
{
var type = value.GetType();
foreach (var propertyInfo in type.GetProperties())
{
var flatAttr = propertyInfo.GetCustomAttribute(typeof(JsonFlatAttribute));
if (flatAttr != null) flatNames.Add(propertyInfo.Name.ToLower());
}
foreach (var token in t.Children())
{
if (token.Type == JTokenType.Property && flatNames.Contains(((JProperty) token).Name.ToLower()))
{
foreach (var child in token.Children())
{
WriteObject(writer, child.Value<object>(), serializer);
}
}
else
{
WriteObject(writer, token.Value<object>(), serializer);
}
}
}
else
{
t.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var value = objectType.GetConstructor(new Type[] { }).Invoke(new object[] { });
return ReadObject(objectType, jObject, value);
}
private object ReadObject(Type objectType, JObject jObject, object value)
{
foreach (var propertyInfo in objectType.GetProperties())
{
var flatAttr = propertyInfo.GetCustomAttribute(typeof(JsonFlatAttribute));
if (flatAttr != null)
{
var obj = propertyInfo.PropertyType.GetConstructor(new Type[] { }).Invoke(new object[] { });
foreach (var property in obj.GetType().GetProperties())
{
var method = jObject.GetType().GetMethod("Value").MakeGenericMethod(property.PropertyType);
var val = method.Invoke(jObject, new object[] { property.Name.ToLower() });
property.SetValue(obj, val);
}
propertyInfo.SetValue(value, obj);
}
else
{
var method = jObject.GetType().GetMethod("Value").MakeGenericMethod(propertyInfo.PropertyType);
var val = method.Invoke(jObject, new object[] { propertyInfo.Name.ToLower() }); // <- System.InvalidCastException: Cannot cast JObject to JToken.
propertyInfo.SetValue(value, val);
}
}
return value;
}
public override bool CanConvert(Type objectType)
{
return true;
}
public override bool CanRead => true;
public override bool CanWrite => true;
}
UPD:更新了代码。InvalidCastException
它适用于某些情况,但对于尝试读取 JSON 时抛出的某些(非平面)属性(请参阅ReadObject
,有一条带有异常消息的注释)。例如,我有 DTO 类
class FooDto { ... }
class BarDto {
public string Name { get; set; }
public FooDto Foo { get; set; }
...
}
Name
已正确处理,但尝试读取Foo
会导致异常。最小的可重现示例:https ://pastebin.com/2gXNiLMz