Усечение строки во время десериализации Json и стойкости ADO - PullRequest
0 голосов
/ 10 января 2019

Я пишу консольное приложение, которое будет работать с API REST, десериализовать JSON в модель C # и затем сохранить эту модель с помощью ADO в таблице базы данных.

Первая проблема заключается в том, что во время первого запуска приложения мы обнаружили, что одно из свойств JSON превысило определение столбца nvarchar(300). Мы увеличили этот столбец до nvarchar(4000), но я понятия не имею, может ли какое-либо из других дюжин свойств строки превысить значение по умолчанию 300, которое я дал им.

К вашему сведению, ошибка SQL, которую я получил, была:

Строка или двоичные данные будут усечены.

Данные для табличного параметра "@Items" не соответствуют табличному типу параметра. Ошибка SQL Server: 8152, состояние: 10

Заявление было прекращено.

... что имеет смысл, если бы я передавал строку длиной 500 в nvarchar(300)

Итак, мое желание: во время десериализации или создания модели в C # я хотел бы обрезать строковые свойства / поля и дать им максимальную длину, прежде чем я нажму свой код персистентности, чтобы я мог на 100% быть уверенным, что мои поля никогда не будут превышать длину nvarchar и никогда не вызовет «ошибку усечения».

Я пытался использовать System.ComponentModel.DataAnnotations и [MaxLength(4000)], но это выглядело только для MVC и проверки ввода во время POSTing формы.

Я думал о создании резервных полей с помощью пользовательских сеттеров, но это означало, что в каждой из моих сущностей было по две строки кода. У меня есть 9 сущностей, и у каждой, вероятно, есть 2 дюжины строк, которые я хочу настроить / усечь.

Итак, вопрос: есть ли какой-нибудь причудливый способ обрезать строки, используя какую-то аннотацию данных NewtonSoft или аннотацию данных C #? Кроме того, есть ли волшебный способ избежать задних полей? Или мне просто создать собственный строковый класс и наследовать от String со свойством max length?

1 Ответ

0 голосов
/ 10 января 2019

Json.Net не имеет встроенных возможностей усечения строк, но вы можете использовать пользовательский ContractResolver в сочетании с пользовательским ValueProvider, чтобы делать то, что вы хотите , ContractResolver будет искать строковые свойства во всех ваших классах и применять к ним ValueProvider, тогда как ValueProvider будет выполнять фактическое усечение во время десериализации. Вы могли бы сделать так, чтобы распознаватель использовал максимальную длину по умолчанию 300 (или любую другую), но также мог бы искать любые атрибуты [MaxLength] (из System.ComponentModel.DataAnnotations), которые вы, возможно, применили к свойствам строки, и использовать эту длину вместо этого как переопределение. , Так что справлюсь с длиной 4000 футляров.

Вот код, который вам понадобится:

public class StringTruncatingPropertyResolver : DefaultContractResolver
{
    public int DefaultMaxLength { get; private set; }

    public StringTruncatingPropertyResolver(int defaultMaxLength)
    {
        DefaultMaxLength = defaultMaxLength;
    }

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

        // Apply a StringTruncatingValueProvider to all string properties
        foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
        {
            var attr = prop.AttributeProvider
                           .GetAttributes(true)
                           .OfType<MaxLengthAttribute>()
                           .FirstOrDefault();
            int maxLength = attr != null ? attr.Length : DefaultMaxLength;
            prop.ValueProvider = new StringTruncatingValueProvider(prop.ValueProvider, maxLength);
        }

        return props;
    }

    class StringTruncatingValueProvider : IValueProvider
    {
        private IValueProvider InnerValueProvider { get; set; }
        private int MaxLength { get; set; }

        public StringTruncatingValueProvider(IValueProvider innerValueProvider, int maxLength)
        {
            InnerValueProvider = innerValueProvider;
            MaxLength = maxLength;
        }

        // GetValue is called by Json.Net during serialization.
        // The target parameter has the object from which to read the string;
        // the return value is a string that gets written to the JSON.
        public object GetValue(object target)
        {
            return InnerValueProvider.GetValue(target);
        }

        // SetValue gets called by Json.Net during deserialization.
        // The value parameter has the string value read from the JSON;
        // target is the object on which to set the (possibly truncated) value.
        public void SetValue(object target, object value)
        {
            string s = (string)value;
            if (s != null && s.Length > MaxLength)
            {
                s = s.Substring(0, MaxLength);
            }
            InnerValueProvider.SetValue(target, s);
        }
    }
}

Чтобы использовать распознаватель, добавьте его в экземпляр JsonSerializerSettings и передайте настройки JsonConvert.DeserializeObject следующим образом:

var settings = new JsonSerializerSettings
{
    ContractResolver = new StringTruncatingPropertyResolver(300)
};
var foo = JsonConvert.DeserializeObject<Foo>(json, settings);

Вот рабочая демонстрация: https://dotnetfiddle.net/YOGsP5

...