我正在使用从Elasticsearch数据库检索数据的Web API。数据库是由一个完全不同的软件填充的,我无法修改。数据中的一个字段metatag.description
应该使用单个值填充,但有时由于上游数据中的错误而最终使用值数组。(我也不能修改这些,这是一个大约200个网站的集合。)
不一致的数据结构以前是通过向模型的相关成员添加自定义JsonConverter
来处理的。
从ASP更新项目后。. NET Core 2.1到3.1,当对象从Elasticsearch反序列化时,自定义JsonConverter
不再被调用。
项目文件引用Microsoft.AspNetCore.Mvc.NewtonsoftJson
包。
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.*" />
Newtonsoft。Json在ConfigureServices()
中初始化:
services.AddControllers().AddNewtonsoftJson();
模型的精简版本看起来像这样(完整的文件包括using Newtonsoft.Json;
)。模型有一个单独的Description
字段,但有时上游数据源发送一个数组而不是单个值。(我们不能合理地修复上游来源)
public class SiteWideSearchResult
{
[Text(Name = "title")]
public string Title { get; set; }
[Text(Name = "metatag.description")]
[JsonConverter(typeof(MetadataDescriptionConverter))]
public string Description { get; set; }
}
JsonConverter
当前看起来是这样的,但是这些行都没有被执行,设置时也没有到达任何断点(这个完整的文件还包括using Newtonsoft.Json;
):
public class MetadataDescriptionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
如上所述,有两个版本的结构被反序列化。JsonConverter
意味着处理这两种数据,并允许在模型中存储(可能更改的)数据版本。
因为它匹配模型,这个版本正确地反序列化:
{
"_index": "myindex",
"_type": "doc",
"_id": "item_id_1",
"_score": 4.3536105,
"_source": {
"metatag.description": "Single description entry",
"title": "This structure works"
}
}
这个版本应该由自定义的JsonConverter
来处理。事实上,它失败了。
{
"_index": "myindex",
"_type": "doc",
"_id": "item_id_2",
"_score": 4.3536105,
"_source": {
"metatag.description": [
"First description line",
"Second description line"
],
"title": "This structure fails"
}
}
第二个结构的错误是expected:'String Begin Token', actual:'['
,这是有意义的,但这就是转换器要解决的问题。除了,它没有被调用。
我怀疑System.Text.Json
序列化器正在被使用,而不是Newtonsoft.Json
的序列化器。
这个答案似乎表明在ConfigureServices
和using
语句中添加Newtonsoft是不够的。然而,答案的简洁让我不清楚需要改变什么(我认为是Razor标记)。我没有发现任何文档建议类似的webapi(可能这是一个属性?这是我已经在使用的)。
我尝试过的事情
如所写,模型和转换器确实与。net 2.1一起工作,但我突然想到,新版本的Nest中的反序列化器可能会停止,因为
Description
被[Text]
装饰。我试过把它改成[Nested]
,但是没有成功。全部删除映射属性,使用
[JsonProperty(PropertyName = "metatag.description", ItemConverter = typeof(MetadataDescriptionConverter))]
。没有区别。在整个
SiteWideSearchResult
模型上使用JsonConverter属性,但这也不执行转换器。(它也不会碰到转换器中的任何断点。)
. NET核心从Newtonsoft.JSON
到原生System.Text.Json.Serialization
的变化是转移注意力。
真正的罪魁祸首是版本7的变化。Elasticsearch的NEST客户端。从7.0版本开始,NEST不再使用Newtonsoft。Json在内部.
对于从Elasticsearch检索数据时反序列化的特定情况,正确的方法似乎是指定一个自定义序列化器,如该页所述。
对于我的特定用例,解决方案是通过在调用ConnectionSettings
构造函数时添加sourceSerializer: JsonNetSerializer.Default
来使用JsonNetSerializer。
var connectionSettings =
new ConnectionSettings(pool, sourceSerializer: JsonNetSerializer.Default);
var client = new ElasticClient(connectionSettings);
另一方面,如果自定义映射的需求扩展到索引创建时,则自定义的"访问者模式映射"可能更合适。