简体   繁体   中英

Deserialize Json.NET with an immutable nested object

I have the following C# class:

public RuleCondition(string field,
                     Operator @operator,
                     RightValueExpression rightValueExpression)
    {
        _operator = @operator;
        _field = field;
        _rightValueExpression = rightValueExpression;
    }
    public RightValueExpression ValueExpression => _rightValueExpression;
    public string Field => _field;
    public Operator Operator => _operator;
}

public sealed class RightValueExpression
{
    private readonly bool _relativeToBaseline;
    private readonly double _value;
    private readonly RightOperator _operator;
    private readonly PercentOrAbsolute _isPercent;
    public RightValueExpression(bool relativeToBaseline,
                                RightOperator @operator,
                                double value,
                                PercentOrAbsolute isPercent)
    {
        _isPercent = isPercent;
        _operator = @operator;
        _value = value;
        _relativeToBaseline = relativeToBaseline;
    }
    public PercentOrAbsolute IsPercent => _isPercent;
    public RightOperator Operator => _operator;
    public double Value => _value;
    public bool RelativeToBaseline => _relativeToBaseline;
}

public enum Operator
{
    GreaterThan,
    EqualTo,
    LessThan,
    GreaterThanEqualTo,
    LessThanEqualTo
}

public enum RightOperator
{
    Plus,
    Minus,
    Times,
    DividedBy
}
public enum PercentOrAbsolute
{
    Percent,
    Absolute
}

When serialized to Json using Json.Net, I get the following:

var ruleCondition = new RuleCondition("Min", Operator.GreaterThan, new RightValueExpression(relativeToBaseline: true, RightOperator.Plus, 50, PercentOrAbsolute.Percent));
var json = JsonConvert.SerializeObject(ruleCondition);

Json:

{
"ValueExpression": {
    "IsPercent": 0,
    "Operator": 0,
    "Value": 50.0,
    "RelativeToBaseline": true
},
"Field": "Min",
"Operator": 0

}

All fine, however deserializing using Json.Net

var des = JsonConvert.DeserializeObject<RuleCondition>(json);

RightValueExpression in RuleCondition is always null.

Do I need a custom deserializer here?

You can implement custom JsonConverter:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);

        var @operator = (Operator)jo["Operator"].Value<int>();
        var field = jo["Field"].Value<string>();

        var isPercent = (PercentOrAbsolute)jo["ValueExpression"]["IsPercent"].Value<int>();
        var rigthOperator = (RightOperator)jo["ValueExpression"]["Operator"].Value<int>();
        var value = jo["ValueExpression"]["Value"].Value<double>();
        var relativeToBaseline = jo["ValueExpression"]["RelativeToBaseline"].Value<bool>();


        RuleCondition result = new RuleCondition(field, @operator, new RightValueExpression(relativeToBaseline, rigthOperator, value, isPercent));

        return result;
    }

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

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

Then you can use it during deserialization:

var des = JsonConvert.DeserializeObject<RuleCondition>(json, new RuleConditionConverter());

If you can live with a public setter on ValueExpression property, you can avoid the specialized converter:

  public class RuleCondition
  {
    public RuleCondition(string field,
                     Operator @operator,
                     RightValueExpression rightValueExpression)
    {
      ValueExpression = rightValueExpression;
      Field = field;
      Operator = @operator;
    }

    public RightValueExpression ValueExpression { get; set; }
    public string Field { get; }
    public Operator Operator { get; }
  }

Alternatively you can rename the ValueExpression property to RightValueExpression :

  public class RuleCondition
  {
    public RuleCondition(string field,
                     Operator @operator,
                     RightValueExpression rightValueExpression)
    {
      RightValueExpression = rightValueExpression;
      Field = field;
      Operator = @operator;
    }

    public RightValueExpression RightValueExpression { get; }
    public string Field { get; }
    public Operator Operator { get; }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM