C#-NewtonSoft JsonConverter-访问其他属性
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C#-NewtonSoft JsonConverter-访问其他属性,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6716字,纯文字阅读大概需要10分钟。
内容图文
![C#-NewtonSoft JsonConverter-访问其他属性](/upload/InfoBanner/zyjiaocheng/686/a567c52fa22c4bff82aba9de73c954f9.jpg)
我需要将小数的输出json格式化为货币,并在要序列化的对象中指定文化,对象可以嵌套,因此无法在序列化器中预设该选项.我当前的操作方式是使用额外的字符串属性来格式化输出.
[JsonIgnore]
public decimal Cost {get;set;}
[JsonIgnore]
public CultureInfo Culture {get;set;}
public string AsCurrency(decimal value) {
return string.Format(this.Culture, "{0:c}", value);
}
[JsonProperty("FormattedCost")]
public string FormatedCost {
get { return this.AsCurrency(this.Cost); }
}
我有很多要处理的属性,我不介意反序列化,另一种语言使用JsonObject填充PDF,因此我需要字符串值.
理想情况下,我想要一个JsonConverter,所以我可以做
[JsonProperty("FormattedCost")]
[JsonConverter(typeof(MyCurrencyConverter))]
public decimal Cost {get;set;}
我的问题是如何访问转换器中包含对象的Culture属性.
public class MyCurrencyConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var culture = // How do I get the Culture from the parent object?
writer.WriteValue(string.format(culture, "{0:c}", (decimal)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(decimal) == objectType;
}
}
根据请求的示例JSON.
每种都有成本和文化的合同类别.
[{ FormattedCost : "£5000.00"}, { FormattedCost : "$8000.00"}, { FormattedCost : "€599.00"}]
实际的对象要复杂得多,具有嵌套资产的多个字段将具有自己的图形.此外,并非所有的小数点都是货币.
我真的不需要为Contract本身编写自定义序列化程序,因为每次属性更改时我都必须对其进行修改.
理想的解决方案是能够使用转换器属性标记某些十进制属性,以便它可以处理它.
我想去的另一种方法是使用十进制的隐式转换为十进制属性创建一个自定义类,但是由于某些属性是根据先前结果计算的属性,因此变得更加复杂.
解决方法
对于我的用例,我有一个解决方法,但是它使用反射在序列化器中获取私有变量.
var binding = BindingFlags.NonPublic | BindingFlags.Instance;
var writer = serializer.GetType()
.GetMethod("GetInternalSerializer", binding)
?.Invoke(serializer, null);
var parent = writer?.GetType()
.GetField("_serializeStack", binding)
?.GetValue(writer) is List<object> stack
&& stack.Count > 1 ? stack[stack.Count - 2] as MyType: null;
在我经过测试的用例中,这为我提供了父对象,但未使用公共API.
解决方法:
您想要做的是在对对象的特定属性值进行序列化时对其进行拦截和修改,同时对所有其他属性使用默认序列化.可以使用custom ContractResolver来完成,当应用特定属性时,该custom ContractResolver会替换相关属性的ValueProvider.
首先,定义以下属性和合同解析器:
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false)]
public class JsonFormatAttribute : System.Attribute
{
public JsonFormatAttribute(string formattingString)
{
this.FormattingString = formattingString;
}
/// <summary>
/// The format string to pass to string.Format()
/// </summary>
public string FormattingString { get; set; }
/// <summary>
/// The name of the underlying property that returns the object's culture, or NULL if not applicable.
/// </summary>
public string CulturePropertyName { get; set; }
}
public class FormattedPropertyContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization)
.AddFormatting();
}
}
public static class JsonContractExtensions
{
class FormattedValueProvider : IValueProvider
{
readonly IValueProvider baseProvider;
readonly string formatString;
readonly IValueProvider cultureValueProvider;
public FormattedValueProvider(IValueProvider baseProvider, string formatString, IValueProvider cultureValueProvider)
{
this.baseProvider = baseProvider;
this.formatString = formatString;
this.cultureValueProvider = cultureValueProvider;
}
#region IValueProvider Members
public object GetValue(object target)
{
var value = baseProvider.GetValue(target);
var culture = cultureValueProvider == null ? null : (CultureInfo)cultureValueProvider.GetValue(target);
return string.Format(culture ?? CultureInfo.InvariantCulture, formatString, value);
}
public void SetValue(object target, object value)
{
// This contract resolver should only be used for serialization, not deserialization, so throw an exception.
throw new NotImplementedException();
}
#endregion
}
public static IList<JsonProperty> AddFormatting(this IList<JsonProperty> properties)
{
ILookup<string, JsonProperty> lookup = null;
foreach (var jsonProperty in properties)
{
var attr = (JsonFormatAttribute)jsonProperty.AttributeProvider.GetAttributes(typeof(JsonFormatAttribute), false).SingleOrDefault();
if (attr != null)
{
IValueProvider cultureValueProvider = null;
if (attr.CulturePropertyName != null)
{
if (lookup == null)
lookup = properties.ToLookup(p => p.UnderlyingName);
var cultureProperty = lookup[attr.CulturePropertyName].FirstOrDefault();
if (cultureProperty != null)
cultureValueProvider = cultureProperty.ValueProvider;
}
jsonProperty.ValueProvider = new FormattedValueProvider(jsonProperty.ValueProvider, attr.FormattingString, cultureValueProvider);
jsonProperty.PropertyType = typeof(string);
}
}
return properties;
}
}
接下来,定义您的对象,如下所示:
public class RootObject
{
[JsonFormat("{0:c}", CulturePropertyName = nameof(Culture))]
public decimal Cost { get; set; }
[JsonIgnore]
public CultureInfo Culture { get; set; }
public string SomeValue { get; set; }
public string SomeOtherValue { get; set; }
}
最后,序列化如下:
var settings = new JsonSerializerSettings
{
ContractResolver = new FormattedPropertyContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(),
},
};
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
笔记:
>由于您没有序列化区域性名称,因此看不到任何反序列化Cost属性的方法.因此,我从SetValue方法中抛出了一个异常.
(而且,即使您要序列化区域性名称,由于JSON对象是standard的无序名称/值对集合,也无法保证区域性名称在反序列化JSON之前不会出现.这可能是与为什么Newtonsoft不提供对父堆栈的访问权限有关.在反序列化期间,不能保证已读取父层次结构中的必需属性-甚至不能保证已经构造了父级.)
>如果必须对合同应用几种不同的自定义规则,请考虑使用How to add metadata to describe which properties are dates in JSON.Net中的ConfigurableContractResolver.
>您可能需要cache the contract resolver以获得最佳性能.
>另一种方法是向父对象添加一个转换器,该转换器通过临时禁用自身,调整返回的JObject并将其写出,从而生成默认的JObject序列化.有关此方法的示例,请参见JSON.Net throws StackOverflowException when using [JsonConvert()]或Can I serialize nested properties to my class in one operation with Json.net?.
>在您写的注释中,在WriteJson内部,我无法弄清楚如何访问父对象及其属性.可以使用一个自定义IValueProvider进行此操作,该IValueProvider返回一个包含父级和值的Tuple或类似的类,该类将与需要此类输入的特定JsonConverter配合使用.不确定是否建议这样做,因为它非常棘手.
工作样本.Net fiddle.
内容总结
以上是互联网集市为您收集整理的C#-NewtonSoft JsonConverter-访问其他属性全部内容,希望文章能够帮你解决C#-NewtonSoft JsonConverter-访问其他属性所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。