自定义序列化

915 发布于: 2021-04-02 读完约需 4 分钟

概述

NEST的默认JSON序列化知道如何正确序列化所有请求和响应类型,以及正确处理实体映射。然而,有时你可能希望通过提供自己的序列化程序或修改NEST的序列化程序的行为来改变默认的行为。NEST提供了自定义序列化的能力。

NEST 6.0或者更早的版本中,序列化依赖的是SimpleJsonNewtonsoft.Json这两个类库,从NEST7.0开始,这两个类库被完全移除了。取而代之的是Utf8Json,这是一个直接使用UTF-8二进制的快速序列化程序。

注入新的序列化器

你可以注入一个单独的仅在序列化_source,_fields时被调用的序列化器,或者用户提供的值被写入和返回的任何地方。

NEST内部,这个序列化器被称作SourceSerializerNEST内部还提供了另外一个叫ReqeustResponseSerializer的序列化器。这个序列化器负责序列化NEST中的请求和响应类型。

如果需要注入自定义的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);

JsonNetSerializer

NEST还提供了一个单独的NEST.JsonNetSerializer包来结合Json.NET实现自定义SourceSerializer,它可以智能地将NEST的类型序列化成内置的RequestResposeSerializerNEST.JsonNetSerializer在以下情况时可能很有用:

  • 你希望使用Json.NET来控制文档和值如何从Elasticsearch存储和检索。
  • 在你的文档中使用类似JObject这样的Newtonsoft.Json.Linq数据类型时。

你可以使用如下方法来配置这个自定义源序列化程序:

var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings =
    new ConnectionSettings(pool, sourceSerializer: JsonNetSerializer.Default);
var client = new ElasticClient(connectionSettings);

JsonNetSerializer.Default只是传递委托的语法糖:

var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(
    pool,
    sourceSerializer: (builtin, settings) => new JsonNetSerializer(builtin, settings));
var client = new ElasticClient(connectionSettings);

JsonNetSerializer的构造函数接受几个方法,这些方法允许你控制JsonSerializerSettings并修改Json.NET解析器的配置,如下:

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);

派生序列化器

如果你想更多地自定义序列化器,你可以从ConnectionSettingsAwareSerializerBase基类派生,然后重写CreateJsonSerializerSettingsModifyContractResolver方法 :

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();
}

MyCustomJsonNetSerializer类中,你可以:

  • 使用Json.NET蛇形属性命名策略
  • 设置序列化时包含null值的属性

以上设置不会影响NEST自己的类型序列化方式。以下示例展示如何使用自定义序列化器来序列化嵌套的类型:

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; }
}

ConnectionSettings构造函数中配置自定义序列化器:

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);

现在,创建一个文档索引:

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
}

序列化出来的JSON字符串遵循我们配置的MyCustomJsonNetSerializer序列化器的规则。

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

发表评论

登录用户才能发表评论, 请 登 录 或者 注册