I am writing a console app that will hit a REST api, deserialize the JSON to a C# model and then persist that model using ADO into a db table.
First issue is that during the first run of the app we found that one of the JSON properties exceeded the column definition of nvarchar(300)
. We increased that column to nvarchar(4000)
, but I have no idea if any of the other dozen string properties might exceed the default of 300 I gave them.
FYI, the SQL error I got was:
String or binary data would be truncated.
The data for table-valued parameter "@Items" doesn't conform to the table type of the parameter. SQL Server error is: 8152, state: 10
The statement has been terminated.
...which makes sense if I was passing in a string with length 500 to an nvarchar(300)
So my desire: during deserialization or model creation in C# I would like to truncate the string properties/fields and give them a max length before I hit my persistence code so that I can ensure with 100% confidence that my fields will never exceed the nvarchar lengths and will never trigger the 'truncation error'.
I tried using System.ComponentModel.DataAnnotations and [MaxLength(4000)]
, but that seemed only for MVC and input validation during form POSTing.
I thought about making backing fields with custom setters, but that means having twice the lines of code in each of my entities. I have 9 entities and each probably has 2 dozen strings that I want to configure/truncate.
So question: is there any fancy way to truncate strings using some sort of NewtonSoft data annotation or a C# data annotation? Also, is there a magic way to avoid having a bazillion back fields? Or should I just make a custom string class and inherit from String that has a max length property?
答案 0 :(得分:2)
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