0

我需要将平面 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

4

0 回答 0