【发布时间】:2021-05-07 12:28:01
【问题描述】:
我有一个包含不同类型块的 json。有简单的块,比如text,反序列化没有问题,所以我不会掩盖它们。问题在于三种块:media、list 和 quiz:
{
"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 网站上找到了一篇文章,但我并不完全了解如何为我的案例实施它:
更新 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 和这篇文章,我已经能够正确反序列化块(希望这段代码没有大问题):
Deserialize JSON array which has mixed values System.Text.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();
}
}
}
现在我正在尝试将其正确序列化回字符串,但尚未发生。
【问题讨论】:
-
能否在代码中也加入你正在使用的类结构和代码?您不需要为此使用自定义转换器,因为 .Net5 库支持对象反序列化。
-
@blockingHD 确定!我已经用代码 sn-p 更新了问题。
-
看起来你有一个多态类型层次结构。
System.Text.Json不支持开箱即用此类层次结构的序列化。您将需要编写一个自定义转换器来做到这一点。为此,请参阅Is polymorphic deserialization possible in System.Text.Json?。这是否回答了您的问题,还是您需要其他帮助? -
@dbc 谢谢你的回答!我在 microsoft guilde to json converters for
System.Text.Json上看到了类似的示例,但老实说,我不太了解如何为我的案例实现它。 -
那么你能把你的代码分解成minimal reproducible example吗?到目前为止您共享的代码甚至无法编译,请参阅dotnetfiddle.net/8UT9r4。如果没有完整的可编译示例,我们所能做的就是将您指向Is polymorphic deserialization possible in System.Text.Json?
标签: c# json serialization deserialization system.text.json