C# .Net Core - System.Text.Json - 是否可以使用多个JsonConverters来反序列化每个JSON段的对象图?

4

我正在尝试学习如何使用System.Text.Json将JSON反序列化为一个不可变的POCO对象,该对象除了接受属性的构造函数外还具有复制构造函数。没有默认构造函数。

阅读dotnet运行时github 问题建议为不可变类型使用JsonConverter自定义反序列化过程。

我已尝试并成功反序列化。但是,我想重构反序列化方法,因为它目前是针对3级对象图(请参见下文)反序列化的单体结构。

此时,我正在尝试了解是否可以为对象图中的每个级别都有一个JsonConverter?例如:

 JsonSerializerOptions serializeOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
            serializeOptions.Converters.Add(new MotionDetectionConverter(logger),
            serializeOptions.Converters.Add(new MotionInfoConverter(logger),
            serializeOptions.Converters.Add(new MotionMatricesConverter(matrices)
);

是否可能将转换器与正确的JSON片段匹配,还是编写一些小帮助类更容易?

  internal sealed class MotionDetectionConverter : JsonConverter<MotionDetection>
    {
        private HashSet<string> _motionDetectionProps;
        private HashSet<string> _motionInfoProps;
        private HashSet<string> _motionMatrixProps;
        private readonly ILogger _log;

        public MotionDetectionConverter(ILogger<MotionDetectionConverter> logger)
        {
            _log = logger;
            _motionMatrixProps = new HashSet<string>(new string[] { "x", "y", "width", "height", "tag", "confidence" });
            _motionDetectionProps = new HashSet<string>(new string[] { "group", "time", "monitorId", "plug", "details" });
            _motionInfoProps = new HashSet<string>(new string[] { "plug", "name", "reason", "matrices", "img", "imgHeight", "imgWidth", "time" });
        }

        public override MotionDetection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.Null) return null;

            using (JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader))
            {
                var jsonObject = jsonDocument.RootElement;

                if (!_motionDetectionProps.IsSupersetOf(jsonObject.EnumerateObject().Select(x => x.Name).AsEnumerable()))
                {
                    throw new JsonException();
                }

                var group = jsonObject.GetProperty("group").ToString();
                var time = jsonObject.GetProperty("time").GetDateTimeOffset();
                var monitorId = jsonObject.GetProperty("monitorId").ToString();
                var plug = jsonObject.GetProperty("plug").ToString();
                var details = jsonObject.GetProperty("details");

                if (!_motionInfoProps.IsSupersetOf(details.EnumerateObject().Select(x => x.Name).AsEnumerable()))
                {
                    throw new JsonException();
                }

                var infoPlug = details.GetProperty("plug").ToString();
                var infoName = details.GetProperty("name").ToString();
                var infoReason = details.GetProperty("reason").ToString();
                var infoImg = details.GetProperty("img").ToString();
                var infoHeight = details.GetProperty("imgHeight").GetInt32();
                var infoWidth = details.GetProperty("imgWidth").GetInt32();
                var infoTime = details.GetProperty("time").GetDateTimeOffset();

                var infoMatrices = details.GetProperty("matrices").EnumerateArray();
                List<MotionLocation> matrices = new List<MotionLocation>();

                while (infoMatrices.MoveNext())
                {
                    if (!_motionMatrixProps.IsSupersetOf(infoMatrices.Current.EnumerateObject().Select(prop => prop.Name).AsEnumerable()))
                    {
                        throw new JsonException();
                    }

                    var x = infoMatrices.Current.GetProperty("x").GetDouble();
                    var y = infoMatrices.Current.GetProperty("y").GetDouble();
                    var width = infoMatrices.Current.GetProperty("width").GetDouble();
                    var height = infoMatrices.Current.GetProperty("height").GetDouble();
                    var tag = infoMatrices.Current.GetProperty("tag").GetString();
                    var confidence = infoMatrices.Current.GetProperty("confidence").GetDouble();

                    matrices.Add(new MotionLocation(x, y, width, height, tag, confidence));
                }

                MotionInfo info = new MotionInfo(infoPlug, infoName, infoReason, matrices, infoImg, infoHeight, infoWidth, infoTime);

                return new MotionDetection(group, time, monitorId, plug, info);
            }
        }

        public override void Write(Utf8JsonWriter writer, MotionDetection motionDetection, JsonSerializerOptions options) =>
                writer.WriteStringValue(@"motionDetection");

                // to complete....
    }
1个回答

2

最初我使用JsonDocument解析整个JSON并从解析的对象图构造目标对象类型。然而,这会读取整个流到结尾,因此很难嵌套转换器以反序列化子类型。

所以我选择使用Utf8JsonReader在读取流的同时构建对象图。请参见以下代码示例。我将保留此问题未回答一段时间,以允许其他建议使用System.Text.Json将JSON反序列化为不可变类型,包括对示例代码的改进。

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

using Microsoft.Extensions.Logging;

using WebApp.Data;


namespace WebApp.Repository.Converters
{
    /**
     *                          {
     *                              group: 'myGroup',
     *                              time: 2020-04-24T13:49:16+00:00,
     *                              monitorId: 'myMonitorId',
     *                              plug: TensorFlow-WithFiltering-And-MQTT,
     *                              details: {
     *                                  plug: TensorFlow-WithFiltering-And-MQTT,
     *                                  name: 'Tensorflow',
     *                                  reason: 'object',
     *                                  matrices: [{
     *                                      "x": 2.313079833984375,
     *                                      "y": 1.0182666778564453,
     *                                      "width": 373.25050354003906,
     *                                      "height": 476.9341278076172,
     *                                      "tag": "person",
     *                                      "confidence": 0.7375929355621338
     *                                  }],
     *                                  img: 'base64',
     *                                  imgHeight: 64,
     *                                  imgWidth: 48,
     *                                  time: 2020-04-24T13:49:16+00:00
     *                              }
     *                          }
     */
    internal static class MotionDetectionPropertyNames
    {
        public const string Details = "details";
        public const string Group = "group";
        public const string MonitorId = "monitorId";
        public const string Plug = "plug";
        public const string Time = "time";
    }

    internal struct Detection
    {
        public MotionInfo details;
        public string group;
        public string monitorId;
        public string plug;
        public DateTimeOffset time;
    }

    internal sealed class MotionDetectionConverter : JsonConverter<MotionDetection>
    {
        private readonly ILogger _log;

        public MotionDetectionConverter(ILogger<MotionDetectionConverter> logger)
        {
            _log = logger;
        }

        public override MotionDetection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            Detection detection = default(Detection);

            while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
            {
                _log.LogInformation(reader.TokenType.ToString());

                switch (reader.TokenType)
                {
                    case JsonTokenType.PropertyName:
                        {
                            string propertyName = reader.GetString();
                            switch (propertyName)
                            {
                                case MotionDetectionPropertyNames.Details:
                                    {
                                        detection.details = ReadDetails(ref reader);
                                        break;
                                    }
                                case MotionDetectionPropertyNames.Group:
                                    {
                                        detection.group = MotionConverterHelper.ReadString(ref reader);
                                        break;
                                    }
                                case MotionDetectionPropertyNames.MonitorId:
                                    {
                                        detection.monitorId = MotionConverterHelper.ReadString(ref reader);
                                        break;
                                    }
                                case MotionDetectionPropertyNames.Plug:
                                    {
                                        detection.plug = MotionConverterHelper.ReadString(ref reader);
                                        break;
                                    }
                                case MotionDetectionPropertyNames.Time:
                                    {
                                        detection.time = MotionConverterHelper.ReadIso8601DateTime(ref reader, propertyName);
                                        break;
                                    }
                                default:
                                    {
                                        MotionConverterHelper.ThrowUnrecognisedProperty(propertyName);
                                        break;
                                    }
                            }
                            break;
                        }
                }
            }

            return new MotionDetection(detection.group, detection.time, detection.monitorId, detection.plug, detection.details);
        }

        public override void Write(Utf8JsonWriter writer, MotionDetection motionDetection, JsonSerializerOptions options) =>
                throw new NotImplementedException("Serialization of MotionDetection objects is not implemented");

        private MotionInfo ReadDetails(ref Utf8JsonReader reader)
        {
            MotionInfo info = default(MotionInfo);

            while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
            {
                switch (reader.TokenType)
                {
                    case JsonTokenType.StartObject:
                        {
                            _log.LogInformation("Reading MotionInfo, handing over to MotionInfo Converter");

                            using (var logFactory = LoggerFactory.Create(builder => builder.AddConsole()))
                            {
                                var logger = logFactory.CreateLogger<MotionInfoConverter>();

                                JsonSerializerOptions opts = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
                                opts.Converters.Add(new MotionInfoConverter(logger));

                                info = JsonSerializer.Deserialize<MotionInfo>(ref reader, opts);
                            }
                            break;
                        }
                }
            }

            if (info is null)
            {
                throw new JsonException("Failed to details property");
            }

            return info;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接