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
77 views
in Technique[技术] by (71.8m points)

c# - How to prevent a single object property from being converted to a DateTime when it is a string

Here is a simplified version of the model I have to work with:

class InputModel
{
    public string Name { get; set; }
    public object Value { get; set; }
}

And the relevant parts of the controller:

class Controller : ApiController
{
    [HttpPut]
    public async Task<IHttpActionResult> Update([FromBody]InputModel model)
    {
        //implementation
    }
}

the Value property of the InputModel class could be of any type, and which type it will be is only known later on, in a piece of legacy code the model is sent to and that i have no control over.

The problem I have occurs with the following json in the request body:

{
    "name": "X",
    "value": "2001-10-17T13:55:11.123"
}

The default behavior is to parse this json so that the Value property gets converted to a DateTime. DateTimes however are handled very differently from strings in the legacy code and data is lost after handling it (for example: the millisecond part is removed when persisting to the database). So when the same value is later requested, the returned value is "2001-10-17T13:55:11" (milliseconds missing).

Of course I can fix this by globally setting this in my web api configuration:

httpConfiguration.Formatters.JsonFormatter.SerializationSettings.DateParseHandling = DateParseHandling.None;

But doing so disables parsing DateTimes also for models in other methods and controllers that have models for which the default behavior is wanted.

What I'm looking for is something like the following (imaginary) code:

class InputModel
{
    public string Name { get; set; }

    [JsonSerializerSettings(DateParseHandling = DateParseHandling.None)]
    public object Value { get; set; }
}

But I can't find out how to achieve this. Any help would be greatly appreciated.

See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

What one can do is to add a custom JsonConverter to the InputModel type to temporarily toggle JsonReader.DateParseHandling to None:

[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
class InputModel
{
    public string Name { get; set; }
    public object Value { get; set; }
}

public class DateParseHandlingConverter : JsonConverter
{
    readonly DateParseHandling dateParseHandling;

    public DateParseHandlingConverter(DateParseHandling dateParseHandling)
    {
        this.dateParseHandling = dateParseHandling;
    }

    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.DateParseHandling;
        try
        {
            reader.DateParseHandling = dateParseHandling;
            existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
            serializer.Populate(reader, existingValue);
            return existingValue;
        }
        finally
        {
            reader.DateParseHandling = old;
        }
    }

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

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

Note that, if the JSON to be deserialized contains nested arrays or objects, all recursively contained values will be parsed with DateParseHandling.None.

One might ask, why not add a converter directly to the property, like so?

class InputModel
{
    public string Name { get; set; }
    [JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
    public object Value { get; set; }
}

It turns out that this does not work because, at the time JsonConverter.ReadJson() is called, the reader has already advanced to the date string and tokenized it as a DateTime. Thus the converter must be applied to the containing type.


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

...