GrayCat
GrayCat

Reputation: 1795

Specifying default value for a property in relation to other property in Json.NET

I am wondering if there is a way to set default value for a property in relation to other property of the same class in Json.NET e.g like this:

public class JsonsoftExample
{
    [JsonProperty(Required = Required.Always)]
    public DateTime Start { get; set; }

    [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    [DefaultValue(Start.AddHours(1))]
    public DateTime End { get; set; }
}

What I am trying to accomplish here is to populate End with DateTime value that is one hour later than Start in cases e.g when deserializing json to domain model and End value is missing or null. Like this:

string json = "{\"Start\": \"2017-01-01T08:00:00+01:00\"}";
var deserialized = JsonConvert.DeserializeObject<JsonsoftExample>(json);

The way I am doing it now is just inspecting later in code if End value is null in my domain object and if it is - populating it with desired value.

Is it feasible the way proposed in code sample or is there maybe a better simpler way except for the manual checking as in paragraph above?

Upvotes: 1

Views: 4767

Answers (2)

dbc
dbc

Reputation: 116980

As specified in the JSON standard, a JSON object is an unordered set of name/value pairs, so in general Json.NET does not allow one property to be set relative to another. Json.NET is a streaming, single-pass deserializer and there's no guarantee which will appear first in the JSON.

However, when your object specifies use of a parameterized constructor via, e.g., the [JsonConstructor] attribute, Json.NET will pre-load the JSON properties and their values then construct the object with the deserialized values. This affords an opportunity to set the End property relative to the Start property:

public partial class JsonsoftExample
{
    public JsonsoftExample() { }

    [JsonConstructor]       
    JsonsoftExample(DateTime start, DateTime? end)
    {
        this.Start = start;
        this.End = end ?? Start.AddHours(1);            
    }

    [JsonProperty(Required = Required.Always)]
    public DateTime Start { get; set; }

    public DateTime End { get; set; }
}

Notes:

  • The names of the constructor arguments must be the same as the names of the properties, modulo case. This is how Json.NET matches constructor arguments with JSON properties.

  • Note that the End property is of type DateTime while the end constructor argument is of type DateTime?. When "End" is missing from the JSON, Json.NET will pass in a null value into the constructor, which can be checked for to properly initialize the End time relative to the Start.

  • If you don't want to serialize the End property when it is exactly one hour later than the start, you can use conditional property serialization:

    public bool ShouldSerializeEnd()
    {
        return End != Start.AddHours(1);
    }
    
  • The constructor does not need to be public when marked with [JsonConstructor]. Properties not passed into the constructor will be populated after construction as per usual deserialization.

Sample fiddle.

Upvotes: 1

Rahul
Rahul

Reputation: 77896

If you are using C# 6 then probably you can do like [* Not sure if this is what you are looking to achieve]

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]    
public DateTime End { get; set; } = Start.AddHours(1);

Upvotes: 1

Related Questions