根据JSON令牌类型有条件地反序列化属性

时间:2017-02-17 07:09:25

标签: json.net

在我的API中,我想让消费者选择将属性作为字符串标识符或对象发送,例如:

{
    "source": "token_xyz"
}

或者

{
    "source": {
        "name": "test"
    }
}

根据类型(字符串或对象),我想在班上设置特定属性:

public class MyRequest
{       
    [JsonProperty("source")]
    public SourceUser SourceUser { get; set; }

    [JsonProperty("source")]
    public string SourceToken { get; set; }
}

正如您所看到的,我尝试使用JsonProperty("source")修饰这两个属性,但不幸的是,这不起作用 - 该对象无法反序列化。

如何根据JSON类型反序列化到适当的属性?

1 个答案:

答案 0 :(得分:2)

Json.NET不支持具有相同合同属性名称的两个属性,可能是因为,如JSON RFC中所述:

  

名称都是唯一的对象在某种意义上是可互操作的   接收该对象的所有软件实现都会同意   名称 - 值映射。当对象中的名称不是   独特的,接收这样一个对象的软件的行为是   不可预测的。

因此,您需要某种custom JsonConverter来反序列化"source"属性。一种简单的方法是将转换器应用于设置&的私有Source属性。根据需要获得SourceUserSourceToken

public class MyRequest
{
    [JsonConverter(typeof(SourceConverter))]
    [JsonProperty]
    object Source
    {
        get
        {
            // Possibly throw an exception if both are non-null?
            return SourceUser ?? (object)SourceToken;
        }
        set
        {
            SourceUser = value as SourceUser;
            SourceToken = value as string;
        }
    }

    [JsonIgnore]
    public SourceUser SourceUser { get; set; }

    [JsonIgnore]
    public string SourceToken { get; set; }
}

class SourceConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException("This converter is intended to be applied with [JsonConverter(typeof(SourceConverter))]");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        else if (reader.TokenType == JsonToken.StartObject)
            return serializer.Deserialize<SourceUser>(reader);
        else
            return (string)JValue.Load(reader);
    }

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

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

示例fiddle