如何将 class 实例转换为 JsonDocument?

How to convert a class instance to JsonDocument?

假设我们有一个如下所示的实体 class:

public class SerializedEntity
{
    public JsonDocument Payload { get; set; }

    public SerializedEntity(JsonDocument payload)
    {
        Payload = payload;
    }
}

根据 npsql,这会生成一个 table,其列 payload 的类型为 jsonb,此 class 是正确的。

现在我想做的是获取 any class 实例并将其作为 payload 存储在此 table 中,例如:

public class Pizza {
    public string Name { get; set; }
    public int Size { get; set; }
}

然后应该可以作为具有以下结构的对象进行检索:

{Name: "name", Size: 10}

所以我需要这样的东西:

var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(someConverter.method(pizza))

使用 System.Text.Json 有点尴尬但可能:

using System.Text.Json;
using System.Text.Json.Serialization;

var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(JsonDocument.Parse(JsonSerializer.Serialize(pizza)));

它自(我认为)v3.0 以来就已内置到 dotnet 核心中,因此您不需要任何额外的第 3 方库。只是不要忘记 usings.

尽管如此(使用异步 API,也许或如 Magnus 建议的那样,使用 SerializeToUtf8Bytes 序列化为二进制文件)可能有一些技巧可以使解析更有效一些。

我还没有找到任何直接从 TobjectJsonDocument 的方法。 而且我不能相信这是不可能的。如果您知道它是如何工作的或添加您的答案,请发表评论。

对其进行序列化,然后将其解析为 JsonDocument。

var doc = JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(
                new Pizza {Name = "Calzone", Size = 10}));

如果您的 EF 实体 (SerializedEntity) 始终将 Pizza 作为其序列化 JSON 文档,那么您只需使用 POCO mapping - 替换 JsonDocument 属性披萨 属性,并将其映射到 jsonb 列。

如果您想要的实际类型有所不同(有时是 Pizza,有时是其他类型),您还可以将 object 属性 映射到 jsonb,然后将您想要的任何内容分配给它。 Npgsql 将在内部将任何对象序列化为 JSON:

class Program
{
    static void Main()
    {
        using var ctx = new BlogContext();
        ctx.Database.EnsureDeleted();
        ctx.Database.EnsureCreated();

        ctx.FoodEntities.Add(new FoodEntity { SomeJsonFood = new Pizza { Name = "Napoletana" } });
        ctx.FoodEntities.Add(new FoodEntity { SomeJsonFood = new Sushi { FishType = "Salmon" } });
        ctx.SaveChanges();
    }
}

public class BlogContext : DbContext
{
    public DbSet<FoodEntity> FoodEntities { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseNpgsql("...");
}

public class FoodEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    [Column(TypeName = "jsonb")]
    public object SomeJsonFood { get; set; }
}

public class Pizza
{
    public string Name { get; set; }
    public int Size { get; set; }
}

public class Sushi
{
    public string FishType { get; set; }
}

我的最终解决方案:

public class SerializedEntity
{
    public object? Payload { get; set; }

    public SerializedEntity(object? payload)
    {
        Payload = payload;
    }
}

及其 EF 配置:

public void Configure(EntityTypeBuilder<SerializedEntity> builder)
{
    builder.Property(n => n.Payload).HasColumnType("jsonb").HasConversion(
        v => JsonConvert.SerializeObject(v,
            new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}),
        v => JsonConvert.DeserializeObject<object?>(v,
            new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}));
}

您可以使用 .NET 6 中添加的 JsonSerializer.SerializeToDocument。在您的情况下,您最终会得到:

var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(JsonSerializer.SerializeToDocument(pizza))