繁体   English   中英

在不同的类上反序列化 json

[英]Deserialize json on different classes

我有一个 json,里面有不同类型的块。 有简单的块,比如text ,反序列化它们没有问题,所以我不会掩盖它们。 问题在于三种块: medialistquiz

 { "blocks": [ { "type": "text", "data": { "text": "my awesome text", "text_truncated": "<<<same>>>" }, "cover": false, "anchor": "" }, { "type": "media", "data": { "items": [ { "title": "title1", "author": "author1", "image": { "type": "image", "data": { "uuid": "eb19f678-3c9f-58f0-90c2-33bcb8237b17", "width": 1024, "height": 756, "size": 448952, "type": "jpg", "color": "d3c58f", "hash": "", "external_service": [] } } }, { "title": "title2", "author": "author2", "image": { "type": "image", "data": { "uuid": "9274038e-1e9b-5cab-9db5-4936ce88a5c9", "width": 750, "height": 563, "size": 164261, "type": "jpg", "color": "b7a58d", "hash": "", "external_service": [] } } } ], "with_background": false, "with_border": false }, "cover": false, "anchor": "" }, { "type": "list", "data": { "items": [ "foo", "bar" ], "type": "UL" }, "cover": false, "anchor": "" }, { "type": "quiz", "data": { "uid": "00bde249ff735f481620328765695", "hash": "29d6bf8fec36eee3", "tmp_hash": "", "title": "When?", "items": { "a16203287650": "Ashita", "a16203287651": "Kinou", "a16203287742": "Ima" }, "is_public": false, "date_created": 1620328765 }, "cover": false, "anchor": "" } ] }

这些块的items属性彼此不同。

对于序列化/反序列化,我使用的是System.Text.Json ,内置于net5.0 虽然我可以将此 json 的items反序列化为JsonElement并进一步使用它,但当我调用JsonSerializer.Deserialize(...); 例如IEnumerable<MediaBlockData>IEnumerable<string>等等,这取决于它是什么type (我的意思是 json 的属性)。

在寻找答案时,我在 mircosoft 网站上找到了一篇文章,但我并不完全了解如何为我的案例实施它:

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0

更新 1

我的块实现:

 public class Block { [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("data")] public JsonElement Data { get; set; } public IBlockData ParsedData { get; set; } [JsonPropertyName("cover")] public bool Cover { get; set; } [JsonPropertyName("anchor")] public string Anchor { get; set; } private Type GetBlockDataType() => Type switch { "audio" => typeof(AudioBlockData), "code" => typeof(CodeBlockData), "delimiter" => typeof(DelimiterBlockData), "header" => typeof(HeaderBlockData), "image" => typeof(ImageBlockData), "incut" => typeof(IncutBlockData), "instagram" => typeof(InstagramBlockData), "link" => typeof(LinkBlockData), "list" => typeof(ListBlockData), "media" => typeof(MediaBlockData), "number" => typeof(NumberBlockData), "person" => typeof(PersonBlockData), "quiz" => typeof(QuizBlockData), "quote" => typeof(QuoteBlockData), "spotify" => typeof(SpotifyBlockData), "telegram" => typeof(TelegramBlockData), "text" => typeof(TextBlockData), "tiktok" => typeof(TikTokBlockData), "tweet" => typeof(TweetBlockData), "universalbox" => typeof(UniversalBoxBlockData), "video" => typeof(VideoBlockData), "yamusic" => typeof(YaMusicBlockData), _ => typeof(object) }; public IBlockData GetBlockData() { var blockType = GetBlockDataType(); return (IBlockData)JsonSerializer.Deserialize(Data.ToString(), blockType); } } public class ListBlockData: IBlockData { [JsonPropertyName("items")] public IEnumerable<string> Items { get; set; } [JsonPropertyName("type")] public string Type { get; set; } } public class QuizBlockData: IBlockData { [JsonPropertyName("uid")] public string Uid { get; set; } [JsonPropertyName("hash")] public string Hash { get; set; } [JsonPropertyName("tmp_hash")] public string TempHash { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("items")] public JsonElement Items { get; set; } [JsonPropertyName("is_public")] public bool IsPublic { get; set; } [JsonPropertyName("date_created")] public long DateCreated { get; set; } } public class MediaBlockData: IBlockData { [JsonPropertyName("items")] public IEnumerable<MediaItem> Items { get; set; } [JsonPropertyName("with_background")] public bool WithBackground { get; set; } [JsonPropertyName("with_border")] public bool WithBorder { get; set; } } public class MediaItem: Block { [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("author")] public string Author { get; set; } [JsonPropertyName("image")] public Block Image { get; set; } }

更新 2

这是包含 json 字符串的最小可重现代码示例:

 using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; namespace ConsoleApp1 { public interface IBlockData { } public class TextBlockData: IBlockData { [JsonPropertyName("text")] public string Text { get; set; } [JsonPropertyName("text_truncated")] public string TextTruncated { get; set; } } public class MediaItem: Block { [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("author")] public string Author { get; set; } [JsonPropertyName("image")] public Block Image { get; set; } } public class QuizBlockData: IBlockData { [JsonPropertyName("uid")] public string Uid { get; set; } [JsonPropertyName("hash")] public string Hash { get; set; } [JsonPropertyName("tmp_hash")] public string TempHash { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("items")] public JsonElement Items { get; set; } // TODO: normal class instead of JsonElement [JsonPropertyName("is_public")] public bool IsPublic { get; set; } [JsonPropertyName("date_created")] public long DateCreated { get; set; } } public class MediaBlockData: IBlockData { [JsonPropertyName("items")] public IEnumerable<MediaItem> Items { get; set; } [JsonPropertyName("with_background")] public bool WithBackground { get; set; } [JsonPropertyName("with_border")] public bool WithBorder { get; set; } } public class ListBlockData: IBlockData { [JsonPropertyName("items")] public IEnumerable<string> Items { get; set; } [JsonPropertyName("type")] public string Type { get; set; } } public class ImageBlockData: IBlockData { [JsonPropertyName("uuid")] public Guid Uuid { get; set; } [JsonPropertyName("width")] public int Width { get; set; } [JsonPropertyName("height")] public int Height { get; set; } [JsonPropertyName("size")] public long Size { get; set; } [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("color")] public string Color { get; set; } [JsonPropertyName("hash")] public string Hash { get; set; } [JsonPropertyName("external_service")] public IEnumerable<ExternalService> ExternalService { get; set; } } public class ExternalService { [JsonPropertyName("name")] public string Name { get; set; } [JsonPropertyName("id")] public string Id { get; set; } } public class Block { [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("data")] public JsonElement Data { get; set; } public IBlockData ParsedData { get; set; } [JsonPropertyName("cover")] public bool Cover { get; set; } [JsonPropertyName("anchor")] public string Anchor { get; set; } private Type GetBlockDataType() => Type switch { "image" => typeof(ImageBlockData), "list" => typeof(ListBlockData), "media" => typeof(MediaBlockData), "quiz" => typeof(QuizBlockData), "text" => typeof(TextBlockData), _ => typeof(object) }; public IBlockData GetBlockData() { var blockType = GetBlockDataType(); return (IBlockData)JsonSerializer.Deserialize(Data.ToString(), blockType); } } public class Root { [JsonPropertyName("blocks")] public IEnumerable<Block> Blocks { get; set; } } internal static class Program { private static void Main() { var json = "{\"blocks\":[{\"type\":\"text\",\"data\":{\"text\":\"my awesome text\",\"text_truncated\":\"<<<same>>>\"},\"cover\":false,\"anchor\":\"\"},{\"type\":\"media\",\"data\":{\"items\":[{\"title\":\"title1\",\"author\":\"author1\",\"image\":{\"type\":\"image\",\"data\":{\"uuid\":\"eb19f678-3c9f-58f0-90c2-33bcb8237b17\",\"width\":1024,\"height\":756,\"size\":448952,\"type\":\"jpg\",\"color\":\"d3c58f\",\"hash\":\"\",\"external_service\":[]}}},{\"title\":\"title2\",\"author\":\"author2\",\"image\":{\"type\":\"image\",\"data\":{\"uuid\":\"9274038e-1e9b-5cab-9db5-4936ce88a5c9\",\"width\":750,\"height\":563,\"size\":164261,\"type\":\"jpg\",\"color\":\"b7a58d\",\"hash\":\"\",\"external_service\":[]}}}],\"with_background\":false,\"with_border\":false},\"cover\":false,\"anchor\":\"\"},{\"type\":\"list\",\"data\":{\"items\":[\"foo\",\"bar\"],\"type\":\"UL\"},\"cover\":false,\"anchor\":\"\"},{\"type\":\"quiz\",\"data\":{\"uid\":\"00bde249ff735f481620328765695\",\"hash\":\"29d6bf8fec36eee3\",\"tmp_hash\":\"\",\"title\":\"When?\",\"items\":{\"a16203287650\":\"Ashita\",\"a16203287651\":\"Kinou\",\"a16203287742\":\"Ima\"},\"is_public\":false,\"date_created\":1620328765},\"cover\":false,\"anchor\":\"\"}]}"; var root = JsonSerializer.Deserialize<Root>(json); // Example on how it can be deserialized afterwards foreach (Block block in root.Blocks) { block.ParsedData = block.GetBlockData(); } } } }

更新 3

感谢@dbc和这篇文章,我已经能够正确地反序列化块(希望这段代码没有重大问题):

反序列化具有混合值 System.Text.JSON 的 JSON 数组

更新代码:

 using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; namespace ConsoleApp1 { public class BlockData { } public class TextBlockData: BlockData { [JsonPropertyName("text")] public string Text { get; set; } [JsonPropertyName("text_truncated")] public string TextTruncated { get; set; } } public class MediaItemBlock: Block { [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("author")] public string Author { get; set; } [JsonPropertyName("image")] public Block Image { get; set; } } public class QuizBlockData: BlockData { [JsonPropertyName("uid")] public string Uid { get; set; } [JsonPropertyName("hash")] public string Hash { get; set; } [JsonPropertyName("tmp_hash")] public string TempHash { get; set; } [JsonPropertyName("title")] public string Title { get; set; } [JsonPropertyName("items")] public Dictionary<string, string> Items { get; set; } [JsonPropertyName("is_public")] public bool IsPublic { get; set; } [JsonPropertyName("date_created")] public long DateCreated { get; set; } } public class MediaBlockData: BlockData { [JsonPropertyName("items")] public IEnumerable<MediaItemBlock> Items { get; set; } [JsonPropertyName("with_background")] public bool WithBackground { get; set; } [JsonPropertyName("with_border")] public bool WithBorder { get; set; } } public class ListBlockData: BlockData { [JsonPropertyName("items")] public IEnumerable<string> Items { get; set; } [JsonPropertyName("type")] public string Type { get; set; } } public class ImageBlockData: BlockData { [JsonPropertyName("uuid")] public Guid Uuid { get; set; } [JsonPropertyName("width")] public int Width { get; set; } [JsonPropertyName("height")] public int Height { get; set; } [JsonPropertyName("size")] public long Size { get; set; } [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("color")] public string Color { get; set; } [JsonPropertyName("hash")] public string Hash { get; set; } [JsonPropertyName("external_service")] public IEnumerable<ExternalService> ExternalService { get; set; } } public class ExternalService { [JsonPropertyName("name")] public string Name { get; set; } [JsonPropertyName("id")] public string Id { get; set; } } public class Block { [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("data")] public BlockData Data { get; set; } [JsonPropertyName("cover")] public bool Cover { get; set; } [JsonPropertyName("anchor")] public string Anchor { get; set; } public static Type GetBlockDataType(string type) => type switch { "image" => typeof(ImageBlockData), "list" => typeof(ListBlockData), "media" => typeof(MediaBlockData), "quiz" => typeof(QuizBlockData), "text" => typeof(TextBlockData), _ => typeof(object) }; } public class Root { [JsonPropertyName("blocks")] public IEnumerable<Block> Blocks { get; set; } } internal static class Program { private static void Main() { var json = "{\"blocks\":[{\"type\":\"text\",\"data\":{\"text\":\"my awesome text\",\"text_truncated\":\"<<<same>>>\"},\"cover\":false,\"anchor\":\"\"},{\"type\":\"media\",\"data\":{\"items\":[{\"title\":\"title1\",\"author\":\"author1\",\"image\":{\"type\":\"image\",\"data\":{\"uuid\":\"eb19f678-3c9f-58f0-90c2-33bcb8237b17\",\"width\":1024,\"height\":756,\"size\":448952,\"type\":\"jpg\",\"color\":\"d3c58f\",\"hash\":\"\",\"external_service\":[]}}},{\"title\":\"title2\",\"author\":\"author2\",\"image\":{\"type\":\"image\",\"data\":{\"uuid\":\"9274038e-1e9b-5cab-9db5-4936ce88a5c9\",\"width\":750,\"height\":563,\"size\":164261,\"type\":\"jpg\",\"color\":\"b7a58d\",\"hash\":\"\",\"external_service\":[]}}}],\"with_background\":false,\"with_border\":false},\"cover\":false,\"anchor\":\"\"},{\"type\":\"list\",\"data\":{\"items\":[\"foo\",\"bar\"],\"type\":\"UL\"},\"cover\":false,\"anchor\":\"\"},{\"type\":\"quiz\",\"data\":{\"uid\":\"00bde249ff735f481620328765695\",\"hash\":\"29d6bf8fec36eee3\",\"tmp_hash\":\"\",\"title\":\"When?\",\"items\":{\"a16203287650\":\"Ashita\",\"a16203287651\":\"Kinou\",\"a16203287742\":\"Ima\"},\"is_public\":false,\"date_created\":1620328765},\"cover\":false,\"anchor\":\"\"}]}"; var options = new JsonSerializerOptions { Converters = { new BlockConverter() } }; var root = JsonSerializer.Deserialize<Root>(json, options); var ser = JsonSerializer.Serialize(root, options); } } public class BlockConverter: JsonConverter<Block> { public override Block Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using JsonDocument doc = JsonDocument.ParseValue(ref reader); // Get type for convert string type = doc.RootElement.GetProperty("type").GetString(); // Create new block with default deserializer Block block = JsonSerializer.Deserialize<Block>(doc.RootElement.GetRawText()); // Warning: recursive for types with blocks inside block.Data = (BlockData)JsonSerializer.Deserialize(doc.RootElement.GetProperty("data").GetRawText(), Block.GetBlockDataType(type), options); return block; } public override void Write(Utf8JsonWriter writer, Block value, JsonSerializerOptions options) { throw new NotImplementedException(); } } }

现在我正在尝试将其正确序列化回字符串,但尚未发生。

这是一个在不使用转换器的情况下对 json 进行反序列化的版本。

var media = new List<MediaBlock>();
var texts = new List<TextBlock>();

using var doc = JsonDocument.Parse(json);
foreach (var block in doc.RootElement.GetProperty("blocks").EnumerateArray())
{
    switch (block.GetProperty("type").GetString())
    {
        case "text": texts.Add(Deserialise<TextBlock>(block.GetProperty("data"))); break;
        case "media": media.Add(Deserialise<MediaBlock>(block.GetProperty("data"))); break;
        // ... 
    }
}

var composite = new 
{
    Texts = texts,// compsite.Texts[0].Text is 'my awesome text'
    Media = media // compsite.Media.Items[0].Author is 'author1'
};

// This is OK, but if you need speed, please have a look at
// https://stackoverflow.com/questions/58138793/system-text-json-jsonelement-toobject-workaround
static T Deserialise<T>(JsonElement e) => JsonSerializer.Deserialize<T>(e.GetRawText(), options: new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

我使用的辅助类是:

class MediaBlock
{
    public List<MediaItem> Items { get; set; }
}

class MediaItem
{
    public string Author { get; set; }
    public string Title { get; set; }
    // ...
}

class TextBlock
{
    public string Text { get; set; }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM