如何使用自定义序列化程序对使用 NEST 和 Elasticsearch 的子文档也序列化类型信息

马丁·伊曼纽尔森

我正在使用https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/custom-serialization.html#_serializing_type_information的示例来获取 elasticsearch 中文档的 $type 信息。

但是,正如页面上所述,这仅返回外部文档的类型信息:

类型信息为外部 MyDocument 实例序列化,但不是为 SubDocuments 集合中的每个 MySubDocument 实例序列化。

所以我现在的问题是,是否有人知道如何获取子文档的类型信息?

我已经尝试使用与 Elasticsearch 分开的示例中相同的 JsonSerializerSettings(使用 LinqPad),并且在那里我也获得了子文档的类型信息:

void Main()
{
    var temp = new ListBlock
    {
        Id = 1,
        Title = "Titel",
        Blocks = new List<BlockContent> {
            new InnerBlock {
                Id = 11,
                MyProperty ="Inner Property"
            },
            new InnerBlock2 {
                Id = 12,
                MyProperty2 = "Inner property 2"
            }
        }
    };

    var serializeOptions = new Newtonsoft.Json.JsonSerializerSettings
    {
        TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All,
        NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
        TypeNameAssemblyFormatHandling = Newtonsoft.Json.TypeNameAssemblyFormatHandling.Simple,
        Formatting = Newtonsoft.Json.Formatting.Indented
    };
    var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(temp, serializeOptions);
    serialized.Dump();
}

public class BlockContent
{
    public int Id { get; set; }
}

public class ListBlock : BlockContent
{
    public string Title { get; set; }
    public List<BlockContent> Blocks { get; set; }
}

public class ListBlock2 : BlockContent
{
    public string Title2 { get; set; }
    public List<BlockContent> Blocks { get; set; }
}

public class InnerBlock : BlockContent
{
    public string MyProperty { get; set; }
}

public class InnerBlock2 : BlockContent
{
    public string MyProperty2 { get; set; }
}

此代码生成以下 json:

{
  "$type": "UserQuery+ListBlock, LINQPadQuery",
  "Title": "Titel",
  "Blocks": {
    "$type": "System.Collections.Generic.List`1[[UserQuery+BlockContent, LINQPadQuery]], System.Private.CoreLib",
    "$values": [
      {
        "$type": "UserQuery+InnerBlock, LINQPadQuery",
        "MyProperty": "Inner Property",
        "Id": 11
      },
      {
        "$type": "UserQuery+InnerBlock2, LINQPadQuery",
        "MyProperty2": "Inner property 2",
        "Id": 12
      }
    ]
  },
  "Id": 1
}

目前使用这些版本:

  • 弹性搜索 7.4.2
  • 巢 7.4.2

更新:

下面由 Russ Cam 提供的解决方案对于响应中包含的数据模型来说就像一个魅力,但是我已经根据我们如何创建索引(使用自动映射)和批量索引初始文档列表的方式整理了一个示例。如果我们排除模型中的 Guids (CategoryIds) 列表,这可以正常工作,但如果我们包含它,则会引发以下异常:

{
    "took": 8,
    "errors": true,
    "items": [{
            "index": {
                "_index": "testindex",
                "_type": "_doc",
                "_id": "1",
                "status": 400,
                "error": {
                    "type": "mapper_parsing_exception",
                    "reason": "failed to parse field [categoryIds] of type [keyword] in document with id '1'. Preview of field's value: '{$values=[], $type=System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib]], System.Private.CoreLib}'",
                    "caused_by": {
                        "type": "illegal_state_exception",
                        "reason": "Can't get text on a START_OBJECT at 1:140"
                    }
                }
            }
        }, {
            "index": {
                "_index": "testindex",
                "_type": "_doc",
                "_id": "2",
                "status": 400,
                "error": {
                    "type": "mapper_parsing_exception",
                    "reason": "failed to parse field [categoryIds] of type [keyword] in document with id '2'. Preview of field's value: '{$values=[], $type=System.Collections.Generic.List`1[[System.Guid, System.Private.CoreLib]], System.Private.CoreLib}'",
                    "caused_by": {
                        "type": "illegal_state_exception",
                        "reason": "Can't get text on a START_OBJECT at 1:141"
                    }
                }
            }
        }
    ]
}

这是一个简单的(.Net 5)控制台应用程序,希望其他人也可以复制这种行为:

using System;
using System.Collections.Generic;
using System.Linq;
using Elasticsearch.Net;
using Nest;
using Nest.JsonNetSerializer;
using Newtonsoft.Json;

namespace ElasticsearchTypeSerializer
{
    internal class Program
    {
        private const string IndexName = "testindex";

        private static void Main(string[] args)
        {
            var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
            var settings = new ConnectionSettings(pool,
                (builtin, settings) => new MySecondCustomJsonNetSerializer(builtin, settings));
            settings.DisableDirectStreaming();
            var client = new ElasticClient(settings);

            CreateIndex(client);
            IndexDocuments(client);
            var documents = GetDocuments(client);
        }

        private static void CreateIndex(IElasticClient client)
        {
            var createIndexResponse = client.Indices.Create(IndexName, x => x.Map<MyDocument>(m => m.AutoMap()));
        }

        private static void IndexDocuments(IElasticClient client)
        {
            var documents = new List<MyDocument>
            {
                new()
                {
                    Id = 1,
                    Name = "My first document",
                    OwnerId = 2,
                    SubDocuments = new List<SubDocument>
                    {
                        new MySubDocument {Id = 11, Name = "my first sub document"},
                        new MySubDocument2 {Id = 12, Description = "my second sub document"}
                    }
                },
                new()
                {
                    Id = 2,
                    Name = "My second document",
                    OwnerId = 3,
                    SubDocuments = new List<SubDocument>
                    {
                        new MySubDocument {Id = 21, Name = "My third sub document"}
                    }
                }
            };

            var bulkIndexResponse = client.Bulk(b => b.Index(IndexName).IndexMany(documents).Refresh(Refresh.True));
        }

        private static IEnumerable<MyDocument> GetDocuments(IElasticClient client)
        {
            var searchResponse = client.Search<MyDocument>(s => s.Index(IndexName).Query(q => q.MatchAll()));
            var documents = searchResponse.Documents.ToList();
            return documents;
        }
    }


    public class MyDocument
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string FilePath { get; set; }
        public int OwnerId { get; set; }
        public List<Guid> CategoryIds { get; set; } = new();
        public List<SubDocument> SubDocuments { get; set; }
    }

    public class SubDocument
    {
        public int Id { get; set; }
    }

    public class MySubDocument : SubDocument
    {
        public string Name { get; set; }
    }

    public class MySubDocument2 : SubDocument
    {
        public string Description { get; set; }
    }


    public class MySecondCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
    {
        public MySecondCustomJsonNetSerializer(IElasticsearchSerializer builtinSerializer,
            IConnectionSettingsValues connectionSettings)
            : base(builtinSerializer, connectionSettings)
        {
        }

        protected override JsonSerializerSettings CreateJsonSerializerSettings()
        {
            return new()
            {
                TypeNameHandling = TypeNameHandling.All,
                NullValueHandling = NullValueHandling.Ignore,
                TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
            };
        }
    }
}

非常感谢有关此问题的任何帮助!

罗斯凸轮

如果您希望在文档上为类型化集合包含类型信息,则可以省略派生合同解析器,这会抑制集合项类型的类型处理

private static void Main()
{
    var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
    var settings = new ConnectionSettings(pool, 
        (builtin, settings) => new MySecondCustomJsonNetSerializer(builtin, settings));
        
    var client = new ElasticClient(settings);

    var document = new MyDocument
    {
        Id = 1,
        Name = "My first document",
        OwnerId = 2,
        SubDocuments = new[]
        {
        new MySubDocument { Name = "my first sub document" },
        new MySubDocument { Name = "my second sub document" },
    }
    };

    var indexResponse = client.IndexDocument(document);

}

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

public class MySecondCustomJsonNetSerializer : ConnectionSettingsAwareSerializerBase
{
    public MySecondCustomJsonNetSerializer(IElasticsearchSerializer builtinSerializer, IConnectionSettingsValues connectionSettings)
        : base(builtinSerializer, connectionSettings) { }

    protected override JsonSerializerSettings CreateJsonSerializerSettings() =>
        new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            NullValueHandling = NullValueHandling.Ignore,
            TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
        };
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何为父类和子类使用单独的自定义json序列化程序?

使用杰克逊可自定义序列化和反序列化

使用 JQuery 序列化和反序列化自定义对象数组

ASP.NET:使用自定义类型的值序列化和反序列化哈希表

如何在Elasticsearch NEST中序列化JToken或JObject类型的属性?

使用自定义序列化程序,如 MessagePack 和 Phoenix Framework

如何使用 drf 精确序列化和创建自定义用户

使用lombok自定义序列化和反序列化的字段名称

如何使用 Nest 从 Json ElasticSearch 获取日志类型和级别?

如何使用`emit_struct`和`emit_struct_field`序列化自定义结构?

Json.NET不会使用自定义getter和不可变类型反序列化属性

使用YamlDotNEt和C#中的自定义类型类反序列化嵌套的YAML

ElasticSearch和NEST:如何从索引中清除所有文档?

为 Python 中的自定义类型添加序列化和反序列化支持

杰克逊自定义序列化和反序列化

自定义JSON.net序列化和反序列化

Go中接口的自定义JSON序列化和反序列化

Konva序列化和反序列化自定义形状

自定义JSON序列化和反序列化

NSMutableArray的自定义类对象的序列化和反序列化

默认序列化和自定义序列化有什么区别?

自定义序列化器和Jackson模式

如何使Json.NET序列化和反序列化还实现IDictionary <string,object>的自定义动态类型的声明属性?

Jackson 的多态序列化/反序列化和自定义序列化器/反序列化器

自定义序列化程序,可为MongoDb处理可为空和不能为空的类型

DRF 中 ManyToManyField 的自定义序列化程序和视图集

Django REST序列化程序和自定义模型字段中的其他属性

Jackson 不会使用自定义序列化程序序列化 null

使用Elasticsearch Python的序列化错误