如何在json序列化时使用DefaultContractResolver用字符串值覆盖复杂类型属性

问题描述

我有一个具有多个属性的类,其中一些属性是复杂类型,那些属性本身具有自己的多个属性,我正在寻找一种方法来仅从复杂类型中获取一个属性进行序列化。

例如我有这 3 个类

class Organization
{
    public Int32 ID {get; set;}
    public string Name {get; set;}
    public Geography Location {get; set;}
}

class Geography 
{
    public Int32 GeoID {get; set;}
    public string Country {get; set;}
    public string City {get; set;}
    public string State {get; set;}
}   

在这里我想序列化组织类对象,它应该只从属性“位置”中获取“国家”以及其他属性,使用来自 NewtonSoft 库的 JsonConvert.SerializeObject 方法,我期望的 json 字符串输出如下。

{
   "ID":1,"Name":"Sales","Location":"India"
}

我不想使用匿名类型作为可序列化对象,因为这个实现我需要为每个使用此属性类型消毒的对象保持通用。

我正在尝试使用 DefaultContractResolver,因为我已经实现了从序列化中排除所选属性,在同一个实现中,我尝试通过覆盖 CreateProperty 方法但它转换失败,我也无法获得要使用新创建的属性设置的值,如下所示。这是一个尝试,我不知道这是否是用于所需功能的正确方法!请提出建议。

protected override JsonProperty CreateProperty(MemberInfo member,MemberSerialization memberSerialization)
{
    var property = base.CreateProperty(member,memberSerialization);
    JsonProperty newproperty = property;

    if (property.PropertyType == typeof(Geography))
    {
        newproperty = new Jsonproperty()
        {
            PropertyName = property.PropertyName,PropertyType = typeof(string)
            //How to get the value from parent property to set with newproperty? 
        };
    }
    return newproperty;
}

解决方法

我为上述功能构建了这个解决方案,一切都按我的预期工作,但不确定这是不是正确的方法。

合同解析器

public class DynamicContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type,HashSet<string>> _serializableProperties;

    public DynamicContractResolver()
    {
        _serializableProperties = new Dictionary<Type,HashSet<string>>();
    

    public void SerializableProperty(Type type,params string[] jsonPropertyNames)
    {
        if (!_serializableProperties.ContainsKey(type))
            _serializableProperties[type] = new HashSet<string>();

        foreach (var prop in jsonPropertyNames)
            _serializableProperties[type].Add(prop);
    }


    protected override IList<JsonProperty> CreateProperties(Type type,MemberSerialization memberSerialization)
    {
        IList<JsonProperty> properties = base.CreateProperties(type,memberSerialization);

        if (_serializableProperties.Any())
        {
            if (_serializableProperties.Any(o => o.Key == type))
            {
                properties = properties.Where(p => IsSerializable(type,p.PropertyName)).ToList();
            }

        }

        return properties;
    }

    private bool IsSerializable(Type type,string jsonPropertyName)
    {
        if (!_serializableProperties.ContainsKey(type))
            return false;

        return _serializableProperties[type].Contains(jsonPropertyName);
    }

} 

自定义转换器

public class GeographyJsonConverter : JsonConverter
{
    private readonly Type[] _types;

    public GeographyJsonConverter ()
    {
        _types = new Type[] { typeof(Geography) };
    }

    public override void WriteJson(JsonWriter writer,object value,JsonSerializer serializer)
    {
        if (value != null && typeof(Geography) == value.GetType())
        {
            Geography obj = (Geography)value;
            JToken t = JToken.FromObject(obj.Country);
            t.WriteTo(writer);
        }
        else
        {
            JToken t = JToken.FromObject(value);
            t.WriteTo(writer);
        }
    }

    public override object ReadJson(JsonReader reader,Type objectType,object existingValue,JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(t => t == objectType);
    }
}

上述自定义设置的使用

   private void btnTestCode_Click(object sender,EventArgs e)
    {
        Organization org = new Organization 
        {
            Name = "Finance",ID= 1,Location = new Geography(){ GeoID = 200,Country = "India",City = "Pune",State ="MH" }
        };

        var changedProps = "Name,Operation";

        var dynamicContractResolver = new DynamicContractResolver();
        dynamicContractResolver.SerializableProperty(typeof(Organization),changedProps.Split(',').ToArray());

        var selected = new JsonSerializerSettings
        {
            ContractResolver = dynamicContractResolver,Converters = { new GeographyJsonConverter() }
        };

     
        string json = JsonConvert.SerializeObject(org,selected);

        Console.WriteLine(json);
    }