NEST默认通过内置的JSON序列化解析器来处理POCO的请求和响应,如果要更改其中的行为或者想提供自己的序列化器,有下面两种方式:
- 自定义序列化器(Custom Serialization)
- 扩展NEST类型(Extending Nest Types)
1.自定义序列化器(Custom Serialization)
在NEST 7.x版本中,SimpleJson和Newtonsoft.Json已经被Utf8Json取代,Utf8Json是直接基于UTF-8工作的快速JSON序列化器,在迁移到Utf8Json的过程中,删除了之前JSON库中提供的一些特性,这些特性已被证明过于繁重,在新版本中将不再使用。下面是对在NEST 7.x 中使用 Utf8Json 的一些说明:
- 序列化格式,由于性能原因,Utf8Json不会对生成的JSON文本进行缩进。
- 不能通过继承来扩展嵌套类型,Utf8Json不支持这种方式。
- Utf8Json会使用Reflection.Emit进行反射,但不是所有的平台都支持Emit,如UWP、Xamarin.iOS和Xamarin.Android。
- Elasticsearch.NET.DynamicResponse进行JSON序列化时将 array 转化为 List<>,SimpleJson将 array 序列化为 object[],出于性能的考虑,Utf8Json会和DynamicResponse保持一致,将 array 转换为 List<>。
- 将JSON中的对象字段反序列化为c# POCO属性时,Utf8Json的限制比Json.NET严格得多,Utf8Json需要JSON字段名和C# POCO类型的属性名大小写完全匹配。
1.1.注入一个序列化器
你可以注入一个单独的序列化程序,用于对源数据进行序列化,在NEST中,将他称为 SourceSerializer。
在NEST内部还有一个内置的序列化程序 RequestResponseSerialize 负责处理请求和响应的序列化,如果没有配置 SourceSerializer,则直接由 RequestResponseSerializer 来处理。
要注入一个 SourceSerializer需要实现 IElasticsearchSerializer 接口。
public class VanillaSerializer : IElasticsearchSerializer
{
public T Deserialize<T>(Stream stream) => throw new NotImplementedException();
public object Deserialize(Type type, Stream stream) => throw new NotImplementedException();
public Task<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
throw new NotImplementedException();
public Task<object> DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
throw new NotImplementedException();
public void Serialize<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented) =>
throw new NotImplementedException();
public Task SerializeAsync<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented,
CancellationToken cancellationToken = default(CancellationToken)) =>
throw new NotImplementedException();
}
在创建 ConnectionSettings 时设置
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(
pool,
sourceSerializer: (builtin, settings) => new VanillaSerializer());
var client = new ElasticClient(connectionSettings);
如果被序列化的POCO类型包含嵌套类型(如下面的类),自定义的序列化程序无法处理,需要有一种方法将嵌套类型的序列化委托给Nest的序列化程序。
public class MyPercolationDocument
{
public QueryContainer Query { get; set; }
public string Category { get; set; }
}
这时,就需要用NEST 提供的 JsonNetSerializer 将嵌套类的序列化委托给NEST的序列化程序。
使用 JsonNetSerializer 的方式如下:
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings =
new ConnectionSettings(pool, sourceSerializer: (builtin, settings) => new JsonNetSerializer(
builtin, settings,
() => new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include },
resolver => resolver.NamingStrategy = new SnakeCaseNamingStrategy()
));
var client = new ElasticClient(connectionSettings);
1.2.派生序列化器
在 JsonNetSerializer 中进行太多设置的话会不太直观,这时可以直接继承 ConnectionSettingsAwareSerializerBase 类并重写 CreateJsonSerializerSettings() 和 ModifyContractResolver() 方法实现一个序列化器。
public class MyFirstCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
{
public MyFirstCustomJsonNetSerializer(IElasticsearchSerializer builtinSerializer, IConnectionSettingsValues connectionSettings)
: base(builtinSerializer, connectionSettings) { }
protected override JsonSerializerSettings CreateJsonSerializerSettings() =>
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include
};
protected override void ModifyContractResolver(ConnectionSettingsAwareContractResolver resolver) =>
resolver.NamingStrategy = new SnakeCaseNamingStrategy();
}
下面演示使用 MyFirstCustomJsonNetSerializer 进行序列化:
//定义POCO
public class MyDocument
{
public int Id { get; set; }
public string Name { get; set; }
public string FilePath { get; set; }
public int OwnerId { get; set; }
public IEnumerable<MySubDocument> SubDocuments { get; set; }
}
public class MySubDocument
{
public string Name { get; set; }
}
//创建连接
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(
pool,
connection: new InMemoryConnection(),
sourceSerializer: (builtin, settings) => new MyFirstCustomJsonNetSerializer(builtin, settings))
.DefaultIndex("my-index");
var client = new ElasticClient(connectionSettings);
//实例化POCO
var document = new MyDocument
{
Id = 1,
Name = "My first document",
OwnerId = 2
};
var ndexResponse = client.IndexDocument(document);
序列化结果:
{
"id": 1,
"name": "My first document",
"file_path": null,
"owner_id": 2,
"sub_documents": null
}
2.扩展 NEST 类型
创建属性,实现 IProperty 接口
public class MyPluginProperty : IProperty
{
IDictionary<string, object> IProperty.LocalMetadata { get; set; }
public string Type { get; set; } = "my_plugin_property";
public PropertyName Name { get; set; }
public MyPluginProperty(string name, string language)
{
this.Name = name;
this.Language = language;
this.Numeric = true;
}
[PropertyName("language")]
public string Language { get; set; }
[PropertyName("numeric")]
public bool Numeric { get; set; }
}
在创建索引时将其添加到属性映射中
var createIndexResponse = _client.Indices.Create("myindex", c => c
.Map<Project>(m => m
.Properties(props => props
.Custom(new MyPluginProperty("fieldName", "dutch"))
)
)
);
会生成下面的JSON请求数据:
{
"mappings": {
"properties": {
"fieldName": {
"type": "my_plugin_property",
"language": "dutch",
"numeric": true
}
}
}
}