Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
517 views
in Technique[技术] by (71.8m points)

c# - Force decimal type in class definition during serialization

I have a class. One of the members of the class is having subtypes as Decimal. Default Json serializer (not directly used. Used by some no-sql libraries for read/write), converts these Decimal values as Double. Since, external library internally serializes the object, I'm looking for a way to add JsonProperty such that it forces FloatParseHandling.Decimal flag. I have found this article. But, there we are specifically setting the flag during serialization which I don't have access to.

class TestData
{
    [JsonProperty(???)] // I need to apply property only at this level.
    public List<Row> rows;
}

// this class is being used by other APIs.
class Row
{ 
    public string myString { get; set; }
    // this will have int, string, 'decimal' types. Decimal type gets auto converted to Double and gets rounded
    public List<dynamic> values { get; set; } 
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

FloatParseHandling specifies how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. (It is not applicable during writing or serialization, when Json.NET should write both decimal and double values with as much precision as necessary.) There is no built-in attribute to temporarily toggle JsonReader.FloatParseHandling during deserialization of a specific property. When deserializing a statically typed property such as

List<decimal> values { get; set; }

this is not an issue because the serializer tells the reader the required floating-point type. Only when deserializing to a dynamic property does this become problematic.

Thus what you can do is to create a custom JsonConverter that temporarily resets the value of JsonReader.FloatParseHandling, then allocates and populates the object being deserialized, like so:

public class FloatParseHandlingConverter : JsonConverter
{
    readonly FloatParseHandling floatParseHandling;

    public FloatParseHandlingConverter(FloatParseHandling floatParseHandling)
    {
        this.floatParseHandling = floatParseHandling;
    }

    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var old = reader.FloatParseHandling;
        try
        {
            reader.FloatParseHandling = floatParseHandling;
            existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
            serializer.Populate(reader, existingValue);
            return existingValue;
        }
        finally
        {
            reader.FloatParseHandling = old;
        }
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then apply it to your rows property as follows:

class TestData
{
    [JsonConverter(typeof(FloatParseHandlingConverter), FloatParseHandling.Decimal)]
    public List<Row> rows;
}

Sample working .Net fiddle.

As an aside, note that, if you have a dynamic property that might have a decimal value, like so:

public class Parent
{
    dynamic value { get; set; }
}

then applying the converter directly to the property will not work. This is because, at the time JsonConverter.ReadJson() is called, the reader has already advanced to the value string and tokenized it as a double. Thus the converter must be applied to a container type or property, e.g.:

[JsonConverter(typeof(FloatParseHandlingConverter), FloatParseHandling.Decimal)]
public class Parent
{
    // [JsonConverter(typeof(FloatParseHandlingConverter), FloatParseHandling.Decimal)] will not work
    dynamic value { get; set; }
}

This dovetails with the specific case of this question since you want the deserialized floating-point values inside Row to be interpreted as decimal only within TestData.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...