Json 反序列化和 ADO 持久性期间的字符串截断
String Truncation during Json Deserialization and ADO Persistance
我正在编写一个控制台应用程序,它将命中 REST api,将 JSON 反序列化为 C# 模型,然后使用 ADO 将该模型保存到数据库 table。
第一个问题是,在应用程序的第一个 运行 期间,我们发现其中一个 JSON 属性超出了 nvarchar(300)
的列定义。我们将该列增加到 nvarchar(4000)
,但我不知道是否有其他十几个字符串属性 可能 超过我给它们的默认值 300。
仅供参考,我得到的 SQL 错误是:
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.
...如果我将长度为 500 的字符串传递给 nvarchar(300)
,这就有意义了
所以我的愿望:在 C# 中反序列化或模型创建期间,我想 t运行cate 字符串 properties/fields 并在我点击我的持久性代码之前给它们一个最大长度,这样我就可以以 100% 的信心确保我的字段永远不会超过 nvarchar 长度并且永远不会触发 'truncation error'.
我尝试使用 System.ComponentModel.DataAnnotations 和 [MaxLength(4000)]
,但这似乎仅适用于表单发布期间的 MVC 和输入验证。
我考虑过使用自定义设置器制作支持字段,但这意味着我的每个实体中的代码行数增加了一倍。我有 9 个实体,每个实体可能有 2 个我想要 configure/truncate.
的字符串
那么问题来了:有什么奇特的方法可以使用某种 NewtonSoft 数据注释或 C# 数据注释来分类字符串吗?运行另外,有没有一种神奇的方法可以避免拥有无数的后台?或者我应该只制作一个自定义字符串 class 并从具有最大长度 属性 的字符串继承?
Json.Net 没有内置的字符串截断功能,但您可以使用自定义的 ContractResolver
in combination with a custom 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
我正在编写一个控制台应用程序,它将命中 REST api,将 JSON 反序列化为 C# 模型,然后使用 ADO 将该模型保存到数据库 table。
第一个问题是,在应用程序的第一个 运行 期间,我们发现其中一个 JSON 属性超出了 nvarchar(300)
的列定义。我们将该列增加到 nvarchar(4000)
,但我不知道是否有其他十几个字符串属性 可能 超过我给它们的默认值 300。
仅供参考,我得到的 SQL 错误是:
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.
...如果我将长度为 500 的字符串传递给 nvarchar(300)
所以我的愿望:在 C# 中反序列化或模型创建期间,我想 t运行cate 字符串 properties/fields 并在我点击我的持久性代码之前给它们一个最大长度,这样我就可以以 100% 的信心确保我的字段永远不会超过 nvarchar 长度并且永远不会触发 'truncation error'.
我尝试使用 System.ComponentModel.DataAnnotations 和 [MaxLength(4000)]
,但这似乎仅适用于表单发布期间的 MVC 和输入验证。
我考虑过使用自定义设置器制作支持字段,但这意味着我的每个实体中的代码行数增加了一倍。我有 9 个实体,每个实体可能有 2 个我想要 configure/truncate.
的字符串那么问题来了:有什么奇特的方法可以使用某种 NewtonSoft 数据注释或 C# 数据注释来分类字符串吗?运行另外,有没有一种神奇的方法可以避免拥有无数的后台?或者我应该只制作一个自定义字符串 class 并从具有最大长度 属性 的字符串继承?
Json.Net 没有内置的字符串截断功能,但您可以使用自定义的 ContractResolver
in combination with a custom 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