使用实现IConvertible的自定义数据类型反序列化问题

时间:2018-12-13 06:17:57

标签: c# json json.net

我有一个类ChargesDetail,并试图反序列化json,如下所示。在这里,我使用的数据类型为Amount

public class ChargesDetail
{
   public double DiscountRate { get; set; }
   public Amount DiscountAmount { get; set; }
}

public class Amount:IConvertible 
{
    private double _val = 0;
    private int _decimal = 5;

    public Amount()
    {
    }

    public Amount(double amount): this()            
    {
       // this.Value = amount;
        _val = Math.Round(amount, _decimal);
    }

    #region IConvertible Members

    // Implementation snipped

    #endregion
}

我的JSON如下:

{ "DiscountRate":0.0, "DiscountAmount":0.0 }

我正试图像这样反序列化:

T result = JsonConvert.DeserializeObject<ChargesDetail>(json);

这给了我一个异常,例如:

  

来自“ System.Double”的无效转换   到“金额”。

     

at System.Convert.DefaultToType(IConvertible value,Type   targetType,IFormatProvider提供程序)   System.Double.System.IConvertible.ToType(类型类型,IFormatProvider   提供程序),位于System.Convert.ChangeType(对象值,类型)   conversionType,IFormatProvider提供程序)位于   Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader   读者,对象值,CultureInfo文化,JsonContract合同,类型   targetType)

如何将json反序列化为此自定义数据类型?

  • 我无法删除IConvertible,因为在某些计算中它会引发System.StackOverflowException。
  • 我无法将数量类型更改为双精度类型,因为同一类型的属性超过100个,并且在“数量”类中进行计算。

4 个答案:

答案 0 :(得分:2)

由于您的问题表明您正在使用,因此可以使用custom JsonConverterAmount序列化为单个十进制值:

public class AmountConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Amount);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Would decimal be more appropriate than double?
        var value = serializer.Deserialize<double?>(reader);
        if (value == null)
            return null;
        return new Amount(value.Value);

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((Amount)value).Value);
    }
}

然后您可以将其应用于模型,如下所示:

[JsonConverter(typeof(AmountConverter))]
public class Amount : IConvertible
{
    private double _val = 0;
    private int _decimal = 5;

    public double Value { get { return _val; } }

    public Amount()
    {

    }

    public Amount(double amount)
        : this()
    {
        // this.Value = amount;
        _val = Math.Round(amount, _decimal);
    }

    #region IConvertible Members
    #endregion
}

或在进行序列化和反序列化时将其添加到JsonSerializerSettings.Converters中:

var settings = new JsonSerializerSettings
{
    Converters = { new AmountConverter() },
};
var = JsonConvert.DeserializeObject<T>(json, settings);

注意:

  1. 如果Amount旨在表示货币数量,则可以考虑从decimal切换到double

    但是,如果这样做,则需要在更高的级别上切换到FloatParseHandling.Decimal,以防止在JsonTextReader进行解析的过程中失去精度。这可以例如完成通过全局设置JsonSerializerSettings.FloatParseHandling或从this answer到{em> Force decimal type in class definition during serialization 抓取FloatParseHandlingConverter并将其应用于父类ChargesDetail,如下所示:

    [JsonConverter(typeof(FloatParseHandlingConverter), FloatParseHandling.Decimal)]
    public class ChargesDetail
    {
        public decimal DiscountRate { get; set; }
        public Amount DiscountAmount { get; set; }
    }
    

演示小提琴here

答案 1 :(得分:0)

您的类Amount未实现接口IConvertable

答案 2 :(得分:0)

在检查JSON时。

{ "FileVersion":"11.03", "ChargesDetail":{ "DiscountRate":0.0, "DiscountAmount":0.0 } } 

public class ChargesDetail
{
   public double DiscountRate { get; set; }
   public Amount DiscountAmount { get; set; }
}

public class Amount:IConvertible 
{ 

}

在这里您需要将DiscountAmount的类型更改为double,因为您要映射的结构不正确。

或者如果您将JSON更改为

{ "FileVersion":"11.03", "ChargesDetail":{ "DiscountRate":0.0, "DiscountAmount":{ "DiscountAmountVar":0.0 }} }

并分类为

public class ChargesDetail
{
   public double DiscountRate { get; set; }
   public Amount DiscountAmount { get; set; }
}

public class Amount:IConvertible 
{ 
   public double DiscountAmountVar{get;set;}
}

然后您之前的映射将起作用。

答案 3 :(得分:0)

在您的课程金额上,您可以删除// Sign in the user with this external login provider if the user already has a login. var result = await _signInManager.ExternalLoginSignInAsync("YourProvider", userIdClaim.Value, isPersistent: false, bypassTwoFactor: true); if (result.Succeeded) { _logger.LogInformation("User logged in with {Name} provider.", "YourProvider"); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); return RedirectToLocal(returnUrl); } if (result.IsLockedOut) { return RedirectToAction(nameof(Lockout)); } else { // If the user does not have an account, then ask the user to create an account. ViewData["ReturnUrl"] = returnUrl; ViewData["LoginProvider"] = "YourProvider"; var email = claims.FirstOrDefault(x => x.Type == ClaimTypes.Upn).Value; return View("ExternalLogin", new ExternalLoginViewModel { Email = email }); } 并仅实现为:

IConvertible

通过本课程和您的其他课程:

public class Amount {
    public decimal DiscountAmountVar { get; set; }
}

此JSON:**** EDIT ****

    public class ChargesDetail
{
   public decimal DiscountRate { get; set; }
   public Amount DiscountAmount { get; set; }
}

**编辑:刚刚意识到您需要删除Json的一个级别。

您应该能够制作JsonConvert。

主要代码:

string json =
            @"{""DiscountRate"":12.0, ""DiscountAmount"":{ ""DiscountAmountVar"":13.0 } }";