C# Newtonsoft.Json使用多个转换器

在使用 Newtonsoft.Json 进行序列化和反序列化时,JsonConverter 提供了一种灵活的方式来定制对象的转换行为。有时候,你可能需要使用多个转换器来处理不同的类型或满足特定的转换需求。本文将详细介绍如何在 C# 中使用 Newtonsoft.Json 应用多个转换器。

目录

  1. 基础知识
  2. 定义多个 JsonConverter
  3. 应用多个转换器的方法
  4. 转换器的优先级和顺序
  5. 处理转换器冲突
  6. 示例代码
  7. 常见问题与解决方案
  8. 总结

基础知识

JsonConverterNewtonsoft.Json 提供的一个抽象类,用于自定义对象的序列化和反序列化行为。通过继承 JsonConverter,你可以控制特定类型或属性如何被转换为 JSON 或从 JSON 还原。

定义多个 JsonConverter

首先,定义多个自定义的 JsonConverter。例如,一个用于处理日期格式,另一个用于处理特定的枚举类型。

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;

public class CustomDateConverter : JsonConverter<DateTime>
{
    private readonly string _dateFormat;

    public CustomDateConverter(string dateFormat)
    {
        _dateFormat = dateFormat;
    }

    public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString(_dateFormat));
    }

    public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        return DateTime.ParseExact((string)reader.Value, _dateFormat, null);
    }
}

public class CustomEnumConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsEnum;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 将枚举转换为字符串
        writer.WriteValue(value.ToString());
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 从字符串转换回枚举
        return Enum.Parse(objectType, (string)reader.Value);
    }
}

应用多个转换器的方法

Newtonsoft.Json 提供了几种方式来应用多个转换器:

  1. 全局应用转换器
  2. 针对特定类型应用转换器
  3. 针对特定属性应用转换器
全局应用转换器

通过 JsonSerializerSettings 将转换器添加到全局设置中,适用于整个应用程序中的所有序列化和反序列化操作。

var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter>
    {
        new CustomDateConverter("yyyy-MM-dd"),
        new CustomEnumConverter()
    }
};

string json = JsonConvert.SerializeObject(yourObject, settings);
var obj = JsonConvert.DeserializeObject<YourType>(json, settings);
针对特定类型应用转换器

通过在序列化或反序列化时指定类型转换器,只针对特定类型应用某个转换器。

string json = JsonConvert.SerializeObject(yourObject, new CustomDateConverter("yyyy-MM-dd"));
var obj = JsonConvert.DeserializeObject<YourType>(json, new CustomDateConverter("yyyy-MM-dd"));
针对特定属性应用转换器

通过在类的属性上使用 [JsonConverter] 特性,指定该属性使用特定的转换器。

public class Person
{
    public string Name { get; set; }

    [JsonConverter(typeof(CustomDateConverter), "yyyy-MM-dd")]
    public DateTime BirthDate { get; set; }

    [JsonConverter(typeof(CustomEnumConverter))]
    public Gender Gender { get; set; }
}

转换器的优先级和顺序

当多个转换器同时适用时,转换器的应用顺序和优先级需要注意:

  1. 属性级转换器 优先于类型级转换器。
  2. 类型级转换器 优先于全局转换器。
  3. 如果多个全局转换器可以处理同一个类型,则按照添加到 Converters 列表中的顺序依次尝试,直到找到一个能够处理的转换器。

处理转换器冲突

当多个转换器可能适用于同一个类型时,可能会导致冲突或意外行为。为避免这种情况,可以:

  • 明确指定转换器,确保每个类型或属性只使用一个转换器。
  • 使用条件逻辑 在转换器内部判断是否处理特定情况。
  • 组织转换器的添加顺序,确保优先级正确。

示例代码

示例 1:全局和特定属性转换器

假设你有一个 Person 类,其中 BirthDate 属性需要使用自定义日期格式,而 Gender 枚举需要转换为字符串。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

public enum Gender
{
    Male,
    Female,
    Other
}

public class Person
{
    public string Name { get; set; }

    [JsonConverter(typeof(CustomDateConverter), "yyyy-MM-dd")]
    public DateTime BirthDate { get; set; }

    [JsonConverter(typeof(CustomEnumConverter))]
    public Gender Gender { get; set; }
}

public class Program
{
    public static void Main()
    {
        var person = new Person
        {
            Name = "John Doe",
            BirthDate = new DateTime(1990, 5, 23),
            Gender = Gender.Male
        };

        // 全局转换器设置(假设还有其他类型需要全局转换器)
        var settings = new JsonSerializerSettings
        {
            Converters = new List<JsonConverter>
            {
                new CustomEnumConverter()
            },
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(person, settings);
        Console.WriteLine("序列化后的 JSON:");
        Console.WriteLine(json);

        var deserializedPerson = JsonConvert.DeserializeObject<Person>(json, settings);
        Console.WriteLine("\n反序列化后的对象:");
        Console.WriteLine($"Name: {deserializedPerson.Name}");
        Console.WriteLine($"BirthDate: {deserializedPerson.BirthDate.ToString("yyyy-MM-dd")}");
        Console.WriteLine($"Gender: {deserializedPerson.Gender}");
    }
}

输出:

{
  "Name": "John Doe",
  "BirthDate": "1990-05-23",
  "Gender": "Male"
}
示例 2:多个类型转换器

假设你有多个不同的类型需要不同的转换器,例如 DateTimeDecimal 类型都需要自定义格式化。

public class CustomDecimalConverter : JsonConverter<decimal>
{
    public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString("F2")); // 保留两位小数
    }

    public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        return decimal.Parse((string)reader.Value);
    }
}

public class FinancialRecord
{
    public string Description { get; set; }

    [JsonConverter(typeof(CustomDecimalConverter))]
    public decimal Amount { get; set; }

    [JsonConverter(typeof(CustomDateConverter), "MM/dd/yyyy")]
    public DateTime Date { get; set; }
}

public class Program
{
    public static void Main()
    {
        var record = new FinancialRecord
        {
            Description = "Salary",
            Amount = 12345.6789m,
            Date = new DateTime(2023, 12, 31)
        };

        var settings = new JsonSerializerSettings
        {
            Converters = new List<JsonConverter>
            {
                new CustomDateConverter("MM/dd/yyyy"),
                new CustomDecimalConverter()
            },
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(record, settings);
        Console.WriteLine("序列化后的 JSON:");
        Console.WriteLine(json);

        var deserializedRecord = JsonConvert.DeserializeObject<FinancialRecord>(json, settings);
        Console.WriteLine("\n反序列化后的对象:");
        Console.WriteLine($"Description: {deserializedRecord.Description}");
        Console.WriteLine($"Amount: {deserializedRecord.Amount}");
        Console.WriteLine($"Date: {deserializedRecord.Date.ToString("MM/dd/yyyy")}");
    }
}

输出:

{
  "Description": "Salary",
  "Amount": "12345.68",
  "Date": "12/31/2023"
}

常见问题与解决方案

1. 转换器未生效

问题原因: 转换器没有正确应用,可能是因为它没有被正确添加到序列化设置中,或者特性使用不当。

解决方案:

  • 确保转换器被正确添加到 JsonSerializerSettings.Converters 列表中。
  • 检查 [JsonConverter] 特性是否正确应用在类或属性上。
  • 确保自定义转换器的 CanConvert 方法正确实现,能够识别目标类型。
2. 转换器冲突

问题原因: 多个转换器适用于同一个类型,导致转换行为不可预测。

解决方案:

  • 明确指定每个类型或属性仅使用一个转换器。
  • 使用更具体的转换器,确保不会有多个转换器同时适用。
  • 调整转换器的添加顺序,以确保优先级正确。
3. 性能问题

问题原因: 使用多个转换器可能会影响序列化和反序列化的性能,尤其是在处理大量数据时。

解决方案:

  • 复用 JsonSerializer 实例,而不是每次都创建新的实例。
  • 优化自定义转换器的实现,避免不必要的操作。
  • 对于大规模数据,考虑使用流式处理(如 JsonTextReaderJsonTextWriter)提高性能。

总结

Newtonsoft.Json 中使用多个 JsonConverter 可以极大地增强序列化和反序列化的灵活性。通过全局设置、类型级别和属性级别的转换器应用,你可以根据需求定制不同的转换行为。然而,需要注意转换器的优先级和避免冲突,以确保转换过程顺利且高效。通过实际项目中的应用和不断优化,你将能够充分发挥 Newtonsoft.Json 的强大功能,处理各种复杂的 JSON 数据转换需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生命不息-学无止境

你的每一份支持都是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值