使用 JsonConverter 在 C# 中自定义 JSON 反序列化

问题描述

我在 .Net Core 中有两个类

Ownership

namespace CustomStoreDatabase.Models
{
    public class Ownership
    {
        public string OwnershipId { get; set; }
        public List<string> TextOutput { get; set; }
        public DateTime DateTime { get; set; }
        public TimeSpan MeanInterval { get; set; }// Like long ticks,TimeSpan.FromTicks(Int64),TimeSpan.Ticks
    }
}

我需要使用 MeanIntervalTimeSpan.FromTicks(Int64) 方法将 TimeSpan.Ticks 显示为长刻度。

我的自定义 JsonConverter

using CustomStoreDatabase.Models;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace CustomStoreDatabase.Util
{
    public class OwnershipJSonConverter : JsonConverter<Ownership>
    {
        public override bool CanConvert(Type typeToConvert)
        {
            return true;
        }

        public override Ownership Read(ref Utf8JsonReader reader,Type typeToConvert,JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            }
            //*******************
            // HOW TO IMPLEMENT?
            //*******************
            //throw new NotImplementedException();
        }

        public override void Write(Utf8JsonWriter writer,Ownership value,JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            if (value != null)
            {
                writer.WriteString("OwnershipId",value.OwnershipId);
                writer.WriteString("TextOutput",JsonSerializer.Serialize(value.TextOutput));
                writer.WriteString("DateTime",JsonSerializer.Serialize(value.DateTime));
                if (value.MeanInterval != null)
                {
                    writer.WriteNumber("MeanInterval",(long)value.MeanInterval.Ticks);
                }
                else
                {
                    writer.WriteNull("MeanInterval");
                }
            }
            writer.WriteEndObject();
        }
    }
}

我不知道如何实现 Read 方法。 如何实现覆盖 Read 方法的自定义反序列化?

如果可以的话,你们建议我另一种 CanConvert 方法的实现,我非常感谢。

解决方法

似乎您只想执行 TimeSpan 的自定义序列化,因为它属于 Ownership,那么为什么不只为 TimeSpan 制作一个转换器,以免自己手动序列化所有其他类属性?:

public class TimeSpanConverter : JsonConverter<TimeSpan>
{
    public override TimeSpan Read(ref Utf8JsonReader reader,Type typeToConvert,JsonSerializerOptions options)
    {
        return TimeSpan.FromTicks(reader.GetInt64());
    }

    public override void Write(Utf8JsonWriter writer,TimeSpan value,JsonSerializerOptions options)
    {
        writer.WriteNumberValue(value.Ticks);
    }
}

然后用 MeanInterval 装饰您的 JsonConverterAttribute 属性:

public class Ownership
{
    public string OwnershipId { get; set; }
    public List<string> TextOutput { get; set; }
    public DateTime DateTime { get; set; }
    [JsonConverter(typeof(TimeSpanConverter))]
    public TimeSpan MeanInterval { get; set; }// Like long ticks,TimeSpan.FromTicks(Int64),TimeSpan.Ticks
}

Try it online

,

我的回答(很长),您可以选择何时应用自定义转换器。 其他相关帖子:post1post2doc

namespace CustomStoreDatabase.Util
{
    public class OwnershipJSonConverter : JsonConverter<Ownership>
    {
        public override bool CanConvert(Type typeToConvert)
        {
            return true;
        }

        public override Ownership Read(ref Utf8JsonReader reader,JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartObject)
            {
                throw new JsonException();
            }
            Ownership value = new Ownership();
            while (reader.Read())
            {
                //Console.WriteLine($"reader.TokenType:{reader.TokenType}");
                if (reader.TokenType == JsonTokenType.EndObject) {
                    //Console.WriteLine($"End Object!");
                    break;
                }
                switch (reader.TokenType)
                {
                    case JsonTokenType.PropertyName:
                        {
                            string propertyName = reader.GetString();
                            //Console.WriteLine($"propertyName:{propertyName}");
                            switch (propertyName)
                            {
                                case "OwnershipId":
                                    {
                                        reader.Read();
                                        if (reader.TokenType != JsonTokenType.Null) {
                                            value.OwnershipId = reader.GetString();
                                        }
                                        break;
                                    }
                                case "TextOutput":
                                    {
                                        reader.Read();
                                        if (reader.TokenType != JsonTokenType.Null)
                                        {
                                            value.TextOutput = JsonSerializer.Deserialize<List<string>>(reader.GetString());
                                        }
                                        break;
                                    }
                                case "DateTime":
                                    {
                                        reader.Read();
                                        if (reader.TokenType != JsonTokenType.Null)
                                        {
                                            value.DateTime = JsonSerializer.Deserialize<DateTime>(reader.GetString());
                                        }
                                        break;
                                    }
                                case "MeanInterval":
                                    {
                                        reader.Read();
                                        if (reader.TokenType != JsonTokenType.Null)
                                        {
                                            value.MeanInterval = TimeSpan.FromTicks(reader.GetInt32());
                                        }
                                        break;
                                    }
                            }
                            break;
                        }
                }
            }
            return value;
        }

        public override void Write(Utf8JsonWriter writer,Ownership value,JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            if (value != null)
            {
                writer.WriteString("OwnershipId",value.OwnershipId);
                writer.WriteString("TextOutput",JsonSerializer.Serialize(value.TextOutput));
                writer.WriteString("DateTime",JsonSerializer.Serialize(value.DateTime));
                if (value.MeanInterval != null)
                {
                    writer.WriteNumber("MeanInterval",(long)value.MeanInterval.Ticks);
                }
                else
                {
                    writer.WriteNull("MeanInterval");
                }
            }
            writer.WriteEndObject();
        }
    }
}

我的测试

public class Program
{
    public static void Main(string[] args)
    {
        var ownershipI = new Ownership() {
            OwnershipId = "--OwnershipId--",TextOutput = new List<string>()
            {
                "carrot","fox","explorer"
            },DateTime = DateTime.Now,MeanInterval = TimeSpan.FromSeconds(-0.05)
        };

        string jsonStringDefault = JsonSerializer.Serialize(ownershipI,new JsonSerializerOptions{
            WriteIndented = true,Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        });
        Console.WriteLine($"jsonStringDefault:{jsonStringDefault}");

        var serializeOptions = new JsonSerializerOptions
        {
            WriteIndented = true,Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,Converters = { new OwnershipJSonConverter() }
        };
        string jsonStringCustomInput = JsonSerializer.Serialize(ownershipI,serializeOptions);
        Console.WriteLine($"jsonStringCustomInput:{jsonStringCustomInput}");
        Console.WriteLine($"Are jsonStringDefault and jsonStringCustomInput Equals?: {jsonStringDefault.Equals(jsonStringCustomInput)}");

        Ownership ownershipO  = JsonSerializer.Deserialize<Ownership>(jsonStringCustomInput,serializeOptions);
        string jsonStringCustomOutput = JsonSerializer.Serialize(ownershipO,serializeOptions);
        Console.WriteLine($"jsonStringCustomOutput:{jsonStringCustomOutput}");

        Console.WriteLine($"Are jsonStringCustomInput and jsonStringCustomOutput Equals?: :{jsonStringCustomInput.Equals(jsonStringCustomOutput)}");
        Console.WriteLine();
    }
}

输出:

jsonStringDefault:{
  "OwnershipId": "--OwnershipId--","TextOutput": [
    "carrot","explorer"
  ],"DateTime": "2021-02-08T03:13:47.0472512-05:00","MeanInterval": {
    "Ticks": -500000,"Days": 0,"Hours": 0,"Milliseconds": -50,"Minutes": 0,"Seconds": 0,"TotalDays": -5.787037037037037E-07,"TotalHours": -1.388888888888889E-05,"TotalMilliseconds": -50,"TotalMinutes": -0.0008333333333333334,"TotalSeconds": -0.05
  }
}
jsonStringCustomInput:{
  "OwnershipId": "--OwnershipId--","TextOutput": "[\"carrot\",\"fox\",\"explorer\"]","DateTime": "\"2021-02-08T03:13:47.0472512-05:00\"","MeanInterval": -500000
}
Are jsonStringDefault and jsonStringCustomInput Equals?: False
jsonStringCustomOutput:{
  "OwnershipId": "--OwnershipId--","MeanInterval": -500000
}
Are jsonStringCustomInput and jsonStringCustomOutput Equals?: :True

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...