我在要求以特定方式序列化对象时遇到了问题,即对象id值成为键,对象的其余部分形成值。
要序列化的简化类:
[JsonConverter(typeof(FieldTypeConvertor))]
public class FieldType {
public string Id { get; set; }
public string Condition { get; set; }
public string FieldType { get; set; }
public string Label { get; set; }
public string Options { get; set; }
}
这是我的JsonConvertorWriteJson
方法:
public override void WriteJson(JsonWriter writer, UmbracoFormFieldDto value, JsonSerializer serializer)
{
var props = value.GetType().GetProperties();
var idProp = props.FirstOrDefault(p => p.Name.Equals("id", StringComparison.OrdinalIgnoreCase));
var key = idProp.GetValue(value, null).ToString();
var newObj = JsonConvert.SerializeObject(value, new JsonSerializerSettings()
{ ContractResolver = new IgnorePropertiesResolver(new[] { "id" }) });
var container = new JObject { { key, newObj } };
container.WriteTo(writer);
}
我明白为什么我最终会出现StackOverflow,但不知道如何避免它,以便生成我需要的输出,如下所示:
"idValueFromOriginalObj": {
"condition": "propValue",
"fieldype": "propValue",
"label": "propValue",
"options": "propValue"
}
从本质上讲,原始对象中id的值将成为序列化对象中的键,而原始对象的其余属性将形成该值。
您的问题是,在JsonConverter.ReadJson()
内部,您试图递归序列化value
对象,但由于转换器是使用[JsonConverter(typeof(TConverter))]
直接应用于类型的,因此会出现堆栈溢出。
有几个选项可以禁用用于递归序列化的转换器JSON.Net在使用[JsonConvert((]时抛出StackOverflowException。但是,由于您已经使用自定义协定冲突解决程序IgnorePropertiesResolver
来忽略名为"id"
的属性,因此您可能会增强冲突解决程序,使其也可以忽略FieldTypeConvertor
类型的转换器。以下应该可以做到:
public class IgnorePropertiesResolver : DefaultContractResolver
{
readonly HashSet<string> propertiesToIgnore;
readonly HashSet<Type> converterTypesToIgnore;
public IgnorePropertiesResolver(IEnumerable<string> propertiesToIgnore, IEnumerable<Type> converterTypesToIgnore) : base() =>
(this.propertiesToIgnore, this.converterTypesToIgnore) =
((propertiesToIgnore ?? throw new ArgumentNullException()).ToHashSet(StringComparer.OrdinalIgnoreCase),
(converterTypesToIgnore ?? throw new ArgumentNullException()).ToHashSet());
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (propertiesToIgnore.Contains(member.Name))
property.Ignored = true;
if (property.Converter != null && converterTypesToIgnore.Contains(property.Converter.GetType()))
property.Converter = null;
return property;
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
if (contract.Converter != null && converterTypesToIgnore.Contains(contract.Converter.GetType()))
contract.Converter = null;
return contract;
}
};
然后修改FieldTypeConvertor
如下:
public sealed class FieldTypeConvertor : JsonConverter<UmbracoFormFieldDto>
{
static readonly IContractResolver innerResolver = new IgnorePropertiesResolver(new [] { "id" }, new [] { typeof(FieldTypeConvertor) })
{
NamingStrategy = new CamelCaseNamingStrategy(),
};
public override void WriteJson(JsonWriter writer, UmbracoFormFieldDto value, JsonSerializer serializer)
{
var props = value.GetType().GetProperties();
var idProp = props.FirstOrDefault(p => p.Name.Equals("id", StringComparison.OrdinalIgnoreCase));
var key = idProp.GetValue(value, null).ToString();
writer.WriteStartObject();
writer.WritePropertyName(key);
JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = innerResolver }).Serialize(writer, value);
writer.WriteEndObject();
}
public override UmbracoFormFieldDto ReadJson(JsonReader reader, Type objectType, UmbracoFormFieldDto existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException();
}
您的模型将根据需要进行序列化:
{
"idValueFromOriginalObj": {
"condition": "propValue",
"fieldType": "propValue",
"label": "propValue",
"options": "propValue"
}
}
注:
Newtonsoft建议您缓存合约解析器以获得最佳性能。
您应该从
DefaultContractResolver
而不是CamelCasePropertyNamesContractResolver
继承,原因在Json.Net:Html Helper方法未重新生成中有说明。出于性能原因,我取消了对
JObject
的中间序列化,而是直接序列化到传入的JsonWriter
。
在这里演示小提琴。