Asp.net web api 访问自定义json转换器内.NET类的自定义属性

Asp.net web api 访问自定义json转换器内.NET类的自定义属性,asp.net-web-api,json.net,Asp.net Web Api,Json.net,在我的项目中,我编写了一个自定义json转换器来修剪string属性中的空白 下面是我们将使用的典型类的示例 public class Candidate { public string CandidateName { get; set; } } 这是我的自定义json转换器 public class StringSanitizingConverter : JsonConverter { public override bool CanConvert(Type o

在我的项目中,我编写了一个自定义json转换器来修剪string属性中的空白

下面是我们将使用的典型类的示例

public class Candidate
{
    public string CandidateName { get; set; }
}
这是我的自定义json转换器

public class StringSanitizingConverter : JsonConverter
{       
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue , JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
            if (reader.Value != null)
            {
                string sanitizedString = (reader.Value as string).Trim();

                if (StringSanitizeOptions.HasFlag(StringSanitizeOptions.ToLowerCase))
                    sanitizedString = sanitizedString.ToLowerInvariant();

                return sanitizedString;
            }

        return reader.Value;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var text = (string)value;
        if (text == null)
            writer.WriteNull();
        else
            writer.WriteValue(text.Trim());
    }
}
使用我的自定义转换器,我现在可以通过使用我的“候选者”作为参数之一修剪发送到动作方法的任何空白来格式化字符串

public void Post(ComplexType complexTypeParameter){
}
到目前为止,一切进展顺利。我后来想增强这个json转换器,以便根据为候选类中的string属性设置的属性格式化string属性。例如,假设我已经这样编写了我的候选类

 public class Candidate
 {
     [StringSanitizingOptions(Option.ToLowerCase)]
     public string CandidateName { get; set; }
 }
如果我想根据json转换器内的自定义属性配置格式化类的字符串属性,我无法访问自定义转换器的ReadJson方法内的该自定义属性及其配置

这是我到目前为止尝试过的,但没有成功

  • objectType

    参数发送到
    ReadJson()
    方法

  • 我试图查看是否可以在
    ReadJson()
    方法中提取属性的父类,以便对该类应用反射以提取给定给其任何属性的自定义属性,但我也无法提取该属性


  • 包含对象的堆栈不可用于,因此您无法在
    ReadJson()
    中执行所需操作

    相反,您可以创建一个应用适当配置的
    StringSanitizingConverter
    实例的实例,该实例基于为其生成合约的对象的属性

    首先,假设您的数据模型、属性和
    JsonConverter
    如下所示(其中我必须修改一些内容以使您的代码能够编译并包含一些额外的测试用例):

    然后,最后您可以反序列化和序列化
    候选者
    ,如下所示:

    var settings = new JsonSerializerSettings
    {
        ContractResolver = new ConfigurableContractResolver
        {
        }.Configure((s, e) => { e.Contract.AddStringConverters(); }),
    };
    
    var candidate = JsonConvert.DeserializeObject<Candidate>(json, settings);
    
    var json2 = JsonConvert.SerializeObject(candidate, Formatting.Indented, settings);
    
    var设置=新的JsonSerializerSettings
    {
    ContractResolver=新配置的ContractResolver
    {
    }.Configure((s,e)=>{e.Contract.AddStringConverters();}),
    };
    var-candidate=JsonConvert.DeserializeObject(json,设置);
    var json2=JsonConvert.SerializeObject(候选、格式化、缩进、设置);
    
    注:

  • 我不知道为什么包含对象的堆栈在
    ReadJson()
    中不可用。可能性包括:

    • 简单
    • 对象是“无序的名称/值对集”,因此在读取属性值时尝试访问包含的.Net对象并不一定有效,因为所需的信息可能尚未读入(父对象甚至可能尚未构造)
  • 由于
    StringSanitizingConverter
    的默认实例应用于为
    string
    本身生成的契约,因此无需将转换器添加到。这反过来可能会导致性能的小幅度提高,因为这将不再被调用

  • 最近在中被标记为已过时,但必须设置为与Json.NET早期版本中的
    JsonProperty.Converter
    相同的值。如果您使用的是11.0.1或更高版本,则应该能够删除该设置

  • 您可能希望获得最佳性能

  • 要在中修改
    JsonSerializerSettings
    ,请参阅或,具体取决于您的需求和使用的框架版本


  • 示例工作.Net FIDLE。

    我想根据json转换器内的自定义属性配置格式化类的字符串属性——我不明白这是什么意思。你能用一句话澄清一下吗?通常,Json.NET是一个基于契约的序列化程序;每种类型都有自己的合同,通常不是由父级签订的。但是,也许您想要类似或的东西?从上面的代码中可以看出,我使用自定义转换器来修剪CandidateName中的空白,这是默认的require行为。基本上,如果属性[StringSanitizingOptions(Option.ToLowerCase)]存在,我希望将CandidateName值转换为小写。我面临的挑战是,我不确定如何访问自定义JsonConverter类的ReadJson方法中CandidateName属性的自定义属性。@dbc我在链接中查看了您的FIDLE代码。显然,您似乎已经编写了用于定制序列化过程的代码。如果我想根据属性中的自定义属性自定义反序列化过程。你能谈谈如何做到这一点吗?非常感谢你的回答。我能够应用这个解决方案来解决我的问题。非常有用。这正是我在星期五想要的“相反,您可以做的是创建一个自定义协定解析程序,根据生成协定的对象的属性应用一个适当配置的StringSanitizingConverter实例。”我从上的代码中学到了这一点,并且能够实现我的目标。:-)
    public static class JsonContractExtensions
    {
        public static JsonContract AddStringConverters(this JsonContract contract)
        {
            if (contract is JsonPrimitiveContract)
            {
                if (contract.UnderlyingType == typeof(string))
                    contract.Converter = new StringSanitizingConverter();
            }
            else if (contract is JsonObjectContract)
            {
                var objectContract = (JsonObjectContract)contract;
                foreach (var property in objectContract.Properties)
                {
                    if (property.PropertyType == typeof(string))
                    {
                        var attr = property.AttributeProvider.GetAttributes(typeof(StringSanitizingOptionsAttribute), true)
                            .Cast<StringSanitizingOptionsAttribute>()
                            .SingleOrDefault();
                        if (attr != null)
                        {
                            property.Converter = property.MemberConverter = new StringSanitizingConverter(attr.StringSanitizeOptions);
                        }
                    }
                }
            }
            return contract;
        }
    }
    
    public class ConfigurableContractResolver : DefaultContractResolver
    {
        // This contract resolver taken from the answer to
        // https://stackoverflow.com/questions/46047308/how-to-add-metadata-to-describe-which-properties-are-dates-in-json-net
        // https://stackoverflow.com/a/46083201/3744182
    
        readonly object contractCreatedPadlock = new object();
        event EventHandler<ContractCreatedEventArgs> contractCreated;
        int contractCount = 0;
    
        void OnContractCreated(JsonContract contract, Type objectType)
        {
            EventHandler<ContractCreatedEventArgs> created;
            lock (contractCreatedPadlock)
            {
                contractCount++;
                created = contractCreated;
            }
            if (created != null)
            {
                created(this, new ContractCreatedEventArgs(contract, objectType));
            }
        }
    
        public event EventHandler<ContractCreatedEventArgs> ContractCreated
        {
            add
            {
                lock (contractCreatedPadlock)
                {
                    if (contractCount > 0)
                    {
                        throw new InvalidOperationException("ContractCreated events cannot be added after the first contract is generated.");
                    }
                    contractCreated += value;
                }
            }
            remove
            {
                lock (contractCreatedPadlock)
                {
                    if (contractCount > 0)
                    {
                        throw new InvalidOperationException("ContractCreated events cannot be removed after the first contract is generated.");
                    }
                    contractCreated -= value;
                }
            }
        }
    
        protected override JsonContract CreateContract(Type objectType)
        {
            var contract = base.CreateContract(objectType);
            OnContractCreated(contract, objectType);
            return contract;
        }
    }
    
    public class ContractCreatedEventArgs : EventArgs
    {
        public JsonContract Contract { get; private set; }
        public Type ObjectType { get; private set; }
    
        public ContractCreatedEventArgs(JsonContract contract, Type objectType)
        {
            this.Contract = contract;
            this.ObjectType = objectType;
        }
    }
    
    public static class ConfigurableContractResolverExtensions
    {
        public static ConfigurableContractResolver Configure(this ConfigurableContractResolver resolver, EventHandler<ContractCreatedEventArgs> handler)
        {
            if (resolver == null || handler == null)
                throw new ArgumentNullException();
            resolver.ContractCreated += handler;
            return resolver;
        }
    }
    
    var settings = new JsonSerializerSettings
    {
        ContractResolver = new ConfigurableContractResolver
        {
        }.Configure((s, e) => { e.Contract.AddStringConverters(); }),
    };
    
    var candidate = JsonConvert.DeserializeObject<Candidate>(json, settings);
    
    var json2 = JsonConvert.SerializeObject(candidate, Formatting.Indented, settings);