我需要执行以下操作,但是我需要在不将属性放在模型类上或污染模型类的情况下进行操作。理想的解决方案将通过JsonSerializerSettings
工作,而不会干扰其他自定义序列化。顺便说一句,以下内容来自这个问题:Custom conversion of specific objects in JSON.NET
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);
}
}
答案 0 :(得分:1)
您可以使用继承自DefaultContractResolver
的{{3}}将转换器应用于特定属性。
首先,将ConfigurableContractResolver
从custom IContractResolver
抓到 this answer :
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;
}
}
然后,创建一种为JsonObjectContract
配置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
的子类,覆盖How to add metadata to describe which properties are dates in JSON.Net并在那里为Person.LastName
硬编码必要的逻辑。但是,创建一个可配置的解析器以允许在运行时将自定义进行合并似乎更有用和可重用。
在AllCapsConverter.WriteJson()
中,使用DefaultContractResolver.CreateProperty
来写大写字符串会更简单:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var upper = ((string)value).ToUpperInvariant();
writer.WriteValue(upper);
}
您可能想writer.WriteValue(string)
以获得最佳性能。
答案 1 :(得分:1)
您可以通过编程方式将JsonConverter
应用于模型类中的一个或多个属性,而无需通过自定义ContractResolver
使用属性。这是一个简单的示例,它将您的AllCapsConverter
应用于您的LastName
类中的Person
属性。 (如果您正在寻找更强大的解决方案,请查看@dbc的answer。我的目的是展示可能可行的最简单示例。)
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; }
}
正在运行的演示:https://dotnetfiddle.net/o4e3WP