从 JsonConverter 运行默认序列化逻辑

问题描述 投票:0回答:3

我有一个

JsonConverter
,根据实例特定的标志,需要

  • 运行自定义序列化逻辑
  • 运行默认的 Json.NET 序列化逻辑

如何从

JsonConverter
运行默认的 Json.NET 序列化逻辑?

谢谢

c# serialization json.net
3个回答
16
投票

这是一个例子。假设您要序列化的类如下所示:

class Foo
{
    public bool IsSpecial { get; set; }
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

IsSpecial
标志用于控制我们是否在转换器中做一些特殊的事情,或者只是让事情自然序列化。你可以这样写你的转换器:

class FooConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Foo).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Foo foo = (Foo)value;
        JObject jo;
        if (foo.IsSpecial)
        {
            // special serialization logic based on instance-specific flag
            jo = new JObject();
            jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
        }
        else
        {
            // normal serialization
            jo = JObject.FromObject(foo);
        }
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

然后,要使用转换器,请将其实例传递给

SerializeObject
方法(例如在设置中)。 (不要用
JsonConverter
属性修饰目标类,否则序列化时会导致无限递归循环。)

class Program
{
    static void Main(string[] args)
    {
        List<Foo> foos = new List<Foo>
        {
            new Foo
            {
                A = "Moe",
                B = "Larry",
                C = "Curly",
                IsSpecial = false
            },
            new Foo
            {
                A = "Huey",
                B = "Dewey",
                C = "Louie",
                IsSpecial = true
            },
        };

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new FooConverter());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(foos, settings);
        Console.WriteLine(json);
    }
}

输出:

[
  {
    "IsSpecial": false,
    "A": "Moe",
    "B": "Larry",
    "C": "Curly"
  },
  {
    "names": "Huey, Dewey, Louie"
  }
]

8
投票

您可以更改 CanWrite 属性以禁用自定义序列化程序。如果对象可以包含相同类型的子对象或者您在多个线程中进行序列化,则这种方法将无法正常工作。

class FooConverter : JsonConverter
{
    bool _canWrite = true;
    public override bool CanWrite
    {
        get { return _canWrite;}
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Foo foo = (Foo)value;
        JObject jo;
        if (foo.IsSpecial)
        {
            // special serialization logic based on instance-specific flag
            jo = new JObject();
            jo.Add("names", string.Join(", ", new string[] { foo.A, foo.B, foo.C }));
        }
        else
        {
            // normal serialization
            _canWrite = false;
            jo = JObject.FromObject(foo);
            _canWrite = true;
        }
        jo.WriteTo(writer);
    }
}

0
投票

我使用了以下 Newtonsoft.Json 转换器来部分自定义反序列化。我的实现演示了以下内容:

  • 避免更改序列化(写入)行为。
  • 更改实例化行为。
  • 避免更改进一步的反序列化行为,即正常填充,尊重所有现有的自定义。

可以调整此示例以有条件地自定义序列化行为。 StackOverflow 上的另一个答案使用了

[ThreadStatic] static bool IsEnabled
override bool CanWrite => IsEnabled
WriteJson
方法将暂时禁用转换器,使用
serializer.Serialize(writer, value)
进行递归,并在
finally
块中重新启用转换器。这样,转换器可以选择跳过影响序列化行为。

public sealed class CustomJsonConverter<
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>
    : JsonConverter<T>
{
    public override bool CanWrite => false;
    public override bool CanRead => true;

    public override void WriteJson(JsonWriter writer, T? value, JsonSerializer serializer)
    {
        throw new NotSupportedException("This type is only used for deserialization.");
    }

    public override T? ReadJson(JsonReader reader, Type objectType, T? existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return default;

        var result = (T)RuntimeHelpers.GetUninitializedObject(typeof(T));
        serializer.Populate(reader, result);
        return result;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.