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

Custom JSON Deserialization in C# with JsonConverter

我在 .Net Core 中有两个 classes

classOwnership

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
    }
}

我需要使用方法 TimeSpan.FromTicks(Int64)TimeSpan.Ticks.

像长刻度一样显示 MeanInterval

我的自定义 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 制作一个转换器并避免手动序列化所有其他 class 属性?:

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);
    }
}

然后用 JsonConverterAttribute:

装饰你的 MeanInterval 属性
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

我的回答(很长的路要走),您可以在应用自定义转换器时选择。 其他相关帖子:post1, post2 and doc.

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();
            }
            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",
    "fox",
    "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--",
  "TextOutput": "[\"carrot\",\"fox\",\"explorer\"]",
  "DateTime": "\"2021-02-08T03:13:47.0472512-05:00\"",
  "MeanInterval": -500000
}
Are jsonStringCustomInput and jsonStringCustomOutput Equals?: :True