【问题标题】:Deserialize json with auto-trimming strings使用自动修剪字符串反序列化 json
【发布时间】:2013-10-16 18:41:45
【问题描述】:

我使用 Newtonsoft.Json 库

有没有办法在反序列化期间从任何字符串数据中修剪空格?

class Program
{
    class Person
    {
        [JsonProperty("name")]
        public string Name;
    }
    static void Main(string[] args)
    {
        var p = JsonConvert.DeserializeObject<Person>(@"{ name: "" John "" }");
        Console.WriteLine("Name is: \"{0}\"", p.Name);
    }
}

添加:

最后,我有了自定义转换器的解决方案。不好,但比使用 Trim() 的属性更好。

如果有人对如何以更自然的方式进行操作有任何想法,请欢迎。

class Program
{
    sealed class TrimAttribute : Attribute
    { }

    class TrimConverter<T> : JsonConverter where T : new()
    {
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var jObject = JObject.Load(reader);
            var obj = new T();
            serializer.Populate(jObject.CreateReader(), obj);

            var props = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public)
                .Where(p => p.FieldType == typeof(string))
                .Where(p => Attribute.GetCustomAttributes(p).Any(u => (Type) u.TypeId == typeof(TrimAttribute)))
                ;

            foreach (var fieldInfo in props)
            {
                var val = (string) fieldInfo.GetValue(obj);
                fieldInfo.SetValue(obj, val.Trim());
            }

            return obj;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType.IsAssignableFrom(typeof (T));
        }
    }

    [JsonConverter(typeof(TrimConverter<Person>))]
    class Person
    {
        [JsonProperty("name")]
        [Trim]
        public string Name;

        [JsonProperty("surname")]
        public string Surname;
    }
    static void Main(string[] args)
    {
        var p = JsonConvert.DeserializeObject<Person>(@"{ name: "" John "", surname: "" Smith "" }");
        Console.WriteLine("Name is: \"{0}\", \"{1}\"", p.Name, p.Surname);
    }
}

【问题讨论】:

  • 为什么数据的字符串里面有多余的空格?那不应该。
  • 蒂姆 - 它不应该,但它在那里...... :(

标签: c# json.net deserialization


【解决方案1】:

你可以自己写JsonConverter:

public class TrimmingConverter : JsonConverter
{
    public override bool CanRead => true;
    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType) => objectType == typeof(string);

    public override object ReadJson(JsonReader reader, Type objectType,
                                    object existingValue, JsonSerializer serializer)
    {
        return ((string)reader.Value)?.Trim();
    }

    public override void WriteJson(JsonWriter writer, object value, 
                                   JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

您可以像这样使用它来应用到所有字符串字段:

var json = @"{ name:"" John "" }"
var p = JsonConvert.DeserializeObject<Person>(json, new TrimmingConverter());
Console.WriteLine("Name is: \"{0}\"", p.Name);
//Name is: "John"

或者您可以仅将其应用于某些字段:

public class Person
{
    [JsonProperty("name")]
    [JsonConverter(typeof(TrimmingConverter))] // <-- that's the important line
    public string Name { get; set; }
    [JsonProperty("other")]
    public string Other { get; set; }
}

var json = @"{ name:"" John "", other:"" blah blah blah "" }"
var p = JsonConvert.DeserializeObject<Person>(json);
Console.WriteLine("Name is: \"{0}\"", p.Name);
Console.WriteLine("Other is: \"{0}\"", p.Other);

//Name is: "John"
//Other is: " blah blah blah "

【讨论】:

  • thanx,看起来很有趣。我想知道,这个转换器怎么会跳过任何其他字段?
  • @SamFisher 它基于CanConvert:目前它将在所有string 字段上执行此操作。如果您愿意,您可以修改CanConvertReadJsonreader 有很多关于您要反序列化的字段的详细信息)以更具体。例如。在您的示例中,Json.net 会询问 CanConvert 是否同时 Personstring - 根据响应,它仅在 string 字段上使用此转换器。
  • @SamFisher 我添加了一个示例,说明如何仅将其应用于某些属性。
【解决方案2】:

上面提供的解决方案对我不起作用。我修改了 Sam Fisher 解决方案并将其与 Timmerz 解决方案结合起来。 how to get both fields and properties in single call via reflection?

public sealed class TrimAttribute : Attribute {
}

public static class TrimConverterExtension {
  public static void SetValue(this MemberInfo member, object property, object value) {
     switch (member.MemberType) {
        case MemberTypes.Property:
           ((PropertyInfo)member).SetValue(property, value, null);
           break;
        case MemberTypes.Field:
           ((FieldInfo)member).SetValue(property, value);
           break;
        default:
           throw new Exception("Property must be of type FieldInfo or PropertyInfo");
     }
  }

  public static object GetValue(this MemberInfo member, object property) {
     switch (member.MemberType) {
        case MemberTypes.Property:
           return ((PropertyInfo)member).GetValue(property, null);
        case MemberTypes.Field:
           return ((FieldInfo)member).GetValue(property);
        default:
           throw new Exception("Property must be of type FieldInfo or PropertyInfo");
     }
  }

  public static Type GetMemberType(this MemberInfo member) {
     switch (member.MemberType) {
        case MemberTypes.Field:
           return ((FieldInfo)member).FieldType;
        case MemberTypes.Property:
           return ((PropertyInfo)member).PropertyType;
        case MemberTypes.Event:
           return ((EventInfo)member).EventHandlerType;
        default:
           throw new ArgumentException("MemberInfo must be if type FieldInfo, PropertyInfo or EventInfo", "member");
     }
  }
}

public class TrimConverter<T> : JsonConverter where T : new() {
  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
     var jObject = JObject.Load(reader);
     var obj = new T();
     serializer.Populate(jObject.CreateReader(), obj);

     //Looks for the trim attribute on the property
     const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
     IEnumerable<MemberInfo> members = objectType.GetFields(bindingFlags).Cast<MemberInfo>()
         .Concat(objectType.GetProperties(bindingFlags))
         .Where(p => p.GetMemberType() == typeof(string))
         .Where(p => Attribute.GetCustomAttributes(p).Any(u => (Type)u.TypeId == typeof(TrimAttribute)))

         .ToArray();

     foreach (var fieldInfo in members) {
        var val = (string)fieldInfo.GetValue(obj);
        if (!string.IsNullOrEmpty(val)) {
           fieldInfo.SetValue(obj, val.Trim());
        }
     }

     return obj;
  }

  public override bool CanConvert(Type objectType) {
     return objectType.IsAssignableFrom(typeof(T));
  }
}

【讨论】:

    【解决方案3】:

    在 .net core 3.1 中,您可以使用 System.Text.Json 来实现此目的。

        /// <summary>
        /// Trim spaces
        /// </summary>
        public class TrimmingConverter : JsonConverter<string>
        {
            /// <summary>
            /// Trim the input string
            /// </summary>
            /// <param name="reader">reader</param>
            /// <param name="typeToConvert">Object type</param>
            /// <param name="options">Existing Value</param>
            /// <returns></returns>
            public override string Read(
              ref Utf8JsonReader reader,
              Type typeToConvert,
              JsonSerializerOptions options) => reader.GetString()?.Trim();
    
            /// <summary>
            /// Trim the output string
            /// </summary>
            /// <param name="writer">Writer</param>
            /// <param name="dateTimeValue">value</param>
            /// <param name="options">serializer</param>
            public override void Write(
                Utf8JsonWriter writer,
                string dateTimeValue,
                JsonSerializerOptions options) => writer.WriteStringValue(dateTimeValue?.Trim());
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-18
      • 2013-05-07
      • 2018-04-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多