为了账号安全,请及时绑定邮箱和手机立即绑定

Json.NET 中仅一个属性的自定义序列化程序,无需更改模型类

Json.NET 中仅一个属性的自定义序列化程序,无需更改模型类

C#
一只斗牛犬 2022-06-12 15:15:15
我需要做类似以下的事情,但我需要在不将属性放在模型类上或以其他方式污染模型类的情况下这样做。理想的解决方案将通过 . 工作JsonSerializerSettings,而不会干扰其他自定义序列化。public class Person{    public string FirstName { get; set; }    [JsonConverter(typeof(AllCapsConverter))]    public string LastName { get; set; }    // more properties here in the real example, some of which nest to properties that use their own JsonConverters.}对于JsonConverter这个玩具示例(内容并不真正相关;相关的是我将它用于属性):public class AllCapsConverter : JsonConverter{    public override bool CanConvert(Type objectType)        => objectType == typeof(string);    public override bool CanRead => false;    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)    {        throw new NotSupportedException();    }    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)    {        var str = value as string;        var upper = str.ToUpperInvariant();        JToken j = JToken.FromObject(upper);        j.WriteTo(writer);    }}通过单元测试:public class PersonSerializationTest{    [Fact]    public void SerializePerson_LastNameCaps()    {        var person = new Person        {            FirstName = "George",            LastName = "Washington"        };        var serialized = JsonConvert.SerializeObject(person);        var expected = @"{""FirstName"":""George"",""LastName"":""WASHINGTON""}";        Assert.Equal(expected, serialized);    }}
查看完整描述

2 回答

?
慕尼黑5688855

TA贡献1848条经验 获得超2个赞

您可以以编程方式将 aJsonConverter应用于模型类中的一个或多个属性,而无需通过 custom 使用属性ContractResolver。这是一个简单的示例,它将您应用于您班级AllCapsConverter中的LastName财产。Person(如果您正在寻找更强大的解决方案,请查看@dbc 的答案。我的目的是展示可能可行的最简单示例。)


public class CustomResolver : DefaultContractResolver

{

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)

    {

        JsonProperty prop = base.CreateProperty(member, memberSerialization);

        if (prop.DeclaringType == typeof(Person) && prop.UnderlyingName == "LastName")

        {

            prop.Converter = new AllCapsConverter();

        }

        return prop;

    }

}

这是更新的测试和Person模型,显示了如何使用解析器:


public class PersonSerializationTest

{

    [Fact]

    public void SerializePerson_LastNameCaps()

    {

        var person = new Person

        {

            FirstName = "George",

            LastName = "Washington"

        };

        var settings = new JsonSerializerSettings

        {

            ContractResolver = new CustomResolver()

        };

        var serialized = JsonConvert.SerializeObject(person, settings);

        var expected = @"{""FirstName"":""George"",""LastName"":""WASHINGTON""}";

        Assert.Equal(expected, serialized);

    }

}


public class Person

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

}


查看完整回答
反对 回复 2022-06-12
?
温温酱

TA贡献1752条经验 获得超4个赞

您可以使用从IContractResolverDefaultContractResolver.


首先,ConfigurableContractResolver从这个答案中获取如何添加元数据来描述 JSON.Net 中哪些属性是日期:


public class ConfigurableContractResolver : DefaultContractResolver

{

    // This contract resolver taken from the answer to

    // https://stackoverflow.com/questions/46047308/how-to-add-metadata-to-describe-which-properties-are-dates-in-json-net

    // https://stackoverflow.com/a/46083201/3744182


    readonly object contractCreatedPadlock = new object();

    event EventHandler<ContractCreatedEventArgs> contractCreated;

    int contractCount = 0;


    void OnContractCreated(JsonContract contract, Type objectType)

    {

        EventHandler<ContractCreatedEventArgs> created;

        lock (contractCreatedPadlock)

        {

            contractCount++;

            created = contractCreated;

        }

        if (created != null)

        {

            created(this, new ContractCreatedEventArgs(contract, objectType));

        }

    }


    public event EventHandler<ContractCreatedEventArgs> ContractCreated

    {

        add

        {

            lock (contractCreatedPadlock)

            {

                if (contractCount > 0)

                {

                    throw new InvalidOperationException("ContractCreated events cannot be added after the first contract is generated.");

                }

                contractCreated += value;

            }

        }

        remove

        {

            lock (contractCreatedPadlock)

            {

                if (contractCount > 0)

                {

                    throw new InvalidOperationException("ContractCreated events cannot be removed after the first contract is generated.");

                }

                contractCreated -= value;

            }

        }

    }


    protected override JsonContract CreateContract(Type objectType)

    {

        var contract = base.CreateContract(objectType);

        OnContractCreated(contract, objectType);

        return contract;

    }

}


public class ContractCreatedEventArgs : EventArgs

{

    public JsonContract Contract { get; private set; }

    public Type ObjectType { get; private set; }


    public ContractCreatedEventArgs(JsonContract contract, Type objectType)

    {

        this.Contract = contract;

        this.ObjectType = objectType;

    }

}


public static class ConfigurableContractResolverExtensions

{

    public static ConfigurableContractResolver Configure(this ConfigurableContractResolver resolver, EventHandler<ContractCreatedEventArgs> handler)

    {

        if (resolver == null || handler == null)

            throw new ArgumentNullException();

        resolver.ContractCreated += handler;

        return resolver;

    }

}

然后,创建一个方法来配置JsonObjectContractfor Person,如下所示:


public static class JsonContractExtensions

{

    public static void ConfigurePerson(this JsonContract contract)

    {

        if (!typeof(Person).IsAssignableFrom(contract.UnderlyingType))

            return;

        var objectContract = contract as JsonObjectContract;

        if (objectContract == null)

            return;

        var property = objectContract.Properties.Where(p => p.UnderlyingName == nameof(Person.LastName)).Single();

        property.Converter = new AllCapsConverter();

    }

}

最后序列化如下:


// Cache the contract resolver statically for best performance.

var resolver = new ConfigurableContractResolver()

    .Configure((s, e) => { e.Contract.ConfigurePerson(); });


var settigs = new JsonSerializerSettings

{

    ContractResolver = resolver,

};


var person = new Person

{

    FirstName = "George",

    LastName = "Washington"

};

var serialized = JsonConvert.SerializeObject(person, settigs);

笔记:


ConfigurableContractResolver与其创建它,不如对那里的必要逻辑进行子类化DefaultContractResolver、覆盖DefaultContractResolver.CreateProperty和硬编码Person.LastName。然而,创建一个允许在运行时组合定制的可配置解析器似乎更有用且可重用。


用它来写你的大写字符串AllCapsConverter.WriteJson()会更简单:writer.WriteValue(string)


public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)

{

    var upper = ((string)value).ToUpperInvariant();

    writer.WriteValue(upper);

}

您可能希望缓存合约解析器以获得最佳性能。


查看完整回答
反对 回复 2022-06-12
  • 2 回答
  • 0 关注
  • 147 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信