пользовательский Newtonsoft JsonConverter для массивов и коллекций для дальнейшей манипуляции - PullRequest
0 голосов
/ 04 июля 2019

Я хочу использовать пользовательский JsonConverter для строковых массивов (или IEnumerable) и выполнять некоторые манипуляции с массивом (фактически удаляя все строки, которые являются нулевыми или пробельными).

Но я уже застрял в ReadJsonметод, не зная, как правильно получить строку [].

Я сделал пользовательский конвертер для простых строк, где проверил JsonToken.String.Но у массивов есть StartArray и EndArray ...

Кто-нибудь, кто уже де / сериализовал свои пользовательские строковые массивы и может мне помочь?


Подробнее:

ЧтоЯ хочу добиться централизованного или необязательного обрезания строки при привязке модели (поэтому мне не нужно это делать на каждом контроллере), и проверка правильности модели на наличие дубликатов выявила бы «строку» и «строку» как дубликат.

Я пытаюсь сделать это как JsonConverters (выкапывая вывод журнала привязки модели, документы .net core, код github .net core, я понял, что конвертер json лучше всего).

Централизованное использованиебыть сконфигурирован в StartUp Json Options:

  public void ConfigureServices(IServiceCollection services)
  {
    services
      .AddMvc()
      .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
      .AddJsonOptions
      (
        options => 
        {
          options.SerializerSettings.Converters.Add(new TrimmedStringConverter());
          options.SerializerSettings.Converters.Add(new CleanStringArrayConverter());
        }
      );
  }

Использование для каждой модели будет выглядеть так:

  public class RequestModel
  {

    [JsonConverter(typeof(TrimmedStringConverter))]
    public string MyValue { get; set; }

    [JsonConverter(typeof(CleanStringArrayConverter))]
    public string[] Entries { get; set; }

  }

Этот вопрос предоставил конвертер для автоматической обрезкиСтроки на привязке модели.Я просто добавил немного соли.

  public class TrimmedStringConverter : JsonConverter
  {

    public bool EmptyStringsAsNull { get; }

    public TrimmedStringConverter()
    {
      EmptyStringsAsNull = true;
    }

    public TrimmedStringConverter(bool emptyStringsAsNull)
    {
      EmptyStringsAsNull = emptyStringsAsNull;
    }

    public override bool CanConvert(Type objectType)
    {
      return objectType == typeof(string);
    }

    private string CleanString(string str)
    {
      if (str == null) return null;

      str = str.Trim();

      if (str.Length == 0 && EmptyStringsAsNull) return null;

      return str;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {

      if (reader.TokenType == JsonToken.String)
      {
        //if (reader.Value != null)
          return CleanString(reader.Value as string);
      }

      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(CleanString(text));
    }

  }

Теперь в моей модели остаются пустые строки или нули в строке [].Который я сейчас пытаюсь автоматически удалить во втором конвертере (или конвертере, который делает то же самое выше, но для строковых массивов, коллекций).

Я просто не могу понять, как правильно обрабатывать сериализацию / десериализацию массивас читателем и сериализатором.


Вот как далеко я продвинулся (спасибо Silvermind).Второй преобразователь для строковых массивов.

Сначала мне удалось использовать глобально зарегистрированный TrimmedStringConverter в CleanStringArrayConverter (проверьте дополнительный код с комментариями).Это работало до тех пор, пока TrimmedStringConverter использовался глобально, а CleanStringArrayConverter использовался для каждой модели.Использование обоих глобально вызывает бесконечные циклы и СЕРВЕРНЫЕ АВАРИИ с исключением Access Violation.

Поэтому я изменил его на эту версию, где оба могут быть зарегистрированы глобально рядом.

К сожалению, это будет работать только длямассивы.

Может ли кто-нибудь из вас найти этот код, использовать его и поделиться своими улучшениями?

  public class CleanStringArrayConverter : JsonConverter
  {

    public bool TrimStrings { get; }
    public bool EmptyStringsAsNull { get; }
    public bool RemoveWhitespace { get; }
    public bool RemoveNulls { get; }
    public bool RemoveEmpty { get; }


    public CleanStringArrayConverter()
    {
      TrimStrings = true;
      EmptyStringsAsNull = true;
      RemoveWhitespace = true;
      RemoveNulls = true;
      RemoveEmpty = true;
    }

    public CleanStringArrayConverter(bool trimStrings = true, bool emptyStringsAsNull = true, bool removeWhitespace = true, bool removeEmpty = true, bool removeNulls = true)
    {
      TrimStrings = trimStrings;
      EmptyStringsAsNull = emptyStringsAsNull;
      RemoveWhitespace = removeWhitespace;
      RemoveNulls = removeNulls;
      RemoveEmpty = removeEmpty;
    }

    private string CleanString(string str)
    {
      if (str == null) return null;

      if (TrimStrings) str = str.Trim();

      if (str.Length == 0 && EmptyStringsAsNull) return null;

      return str;
    }

    private string[] CleanStringCollection(IEnumerable<string> strings)
    {
      if (strings == null) return null;

      return strings
        .Select(s => CleanString(s))
        .Where
        (
          s =>
          {
            if (s == null) return !RemoveNulls;
            else if (s.Equals(string.Empty)) return !RemoveEmpty;
            else if (string.IsNullOrWhiteSpace(s)) return !RemoveWhitespace;
            else return true;
          }
        )
        .ToArray();
    }

    public override bool CanConvert(Type objectType)
    {
      return objectType.IsArray && objectType.GetElementType() == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {

      string[] arr = null;            // use null as default value
      //string[] arr = new string[];  // use empty array as default value

      // deserialze the array
      if (reader.TokenType != JsonToken.Null)
      {
        if (reader.TokenType == JsonToken.StartArray)
        {
          // this one respects other registered converters (e.g. the TrimmedStringConverter)
          // but causes server crashes when used globally due to endless loops
          //arr = serializer.Deserialize<string[]>(reader);

          // this doesn't respect others!!!
          JToken token = JToken.Load(reader);
          arr = token.ToObject<string[]>();
        }
      }

      // clean up the array
      if (arr != null) arr = CleanStringCollection(arr);

      return arr;

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

      string[] arr = (string[])value;

      if (value == null)
      {
        writer.WriteNull();
        return;
      }

      arr = CleanStringCollection(arr);

      // endless loops and server crashes!!!
      //serializer.Serialize(writer, arr);


      writer.WriteStartArray();
      string v;
      foreach(string s in arr)
      {
        v = CleanString(s);
        if (v == null)
            writer.WriteNull();
        else
            writer.WriteValue(v);
      }

      writer.WriteEndArray();
    }

  }

1 Ответ

1 голос
/ 04 июля 2019

Это в основном та же идея:

internal sealed class TrimmedStringCollectionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsArray && objectType.GetElementType() == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (existingValue is null)
        {
             // Returning empty array???
             return new string[0];
        }

        var array = (string[])existingValue;
        return array.Where(s => !String.IsNullOrEmpty(s)).ToArray();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value);
    }
}

Возможно, вы захотите сделать то же самое для части записи.

...