簡體   English   中英

實施ASP.NET Web API可選參數

[英]Implementing ASP.NET Web API Optional Parameters

我需要能夠區分未提供的鍵和null。

JSON的示例為:

# key not specified
{} 

# key specified but null
{'optionalKey' : null}

# key specified and is valid
{'optionalKey' : 123}

為了區分是否缺少鍵和null,我創建了一個通用的Optional類,該類包裝了每個字段,但這需要編寫一個自定義JsonConverter和DefaultContractResolver來拼合JSON /解壓縮OptionalType(為每個字段發送嵌套JSON是不可選項)。

我已經設法創建一個LINQPad腳本來做到這一點,但我不禁想到,必須有一種不涉及反射的簡便方法?

void Main()
{
    //null
    Settings settings = null;
    JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();

    settings = new Settings();

    // no key {}
    settings.OptionalIntegerSetting = null;
    JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();

    // null key {\"OptionalIntegerSetting\" : null}
    settings.OptionalIntegerSetting = new Optional<uint?>(); // assigning this to null assigns the optional type class, it does not use the implict operators.
    JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();

    // has value {\"OptionalIntegerSetting\" : 123}
    settings.OptionalIntegerSetting = 123;
    JsonConvert.SerializeObject(settings, new JsonSerializerSettings() { ContractResolver = new ShouldSerializeContractResolver() }).Dump();

    JsonConvert.DeserializeObject<Settings>("{}").Dump();
    JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : null}").Dump();
    JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : '123'}").Dump(); // supplying 'a string' instead of '123' currently breaks OptionalConverter.ReadJson 
}

public class Settings
{
    public Optional<uint?> OptionalIntegerSetting { get; set; }
}

[JsonConverter(typeof(OptionalConverter))]
public class Optional<T>
{
    public T Value { get; set; }

    public Optional() { }

    public Optional(T value)
    {
        Value = value;
    }

    public static implicit operator Optional<T>(T t)
    {
        return new Optional<T>(t);
    }

    public static implicit operator T(Optional<T> t)
    {
        return t.Value;
    }
}

// Provides a way of populating the POCO Resource model with CanSerialise proerties at the point just before serialisation.
// This prevents having to define a CanSerialiseMyProperty method for each property.
public class ShouldSerializeContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Optional<>))
        {
            // add an additional ShouldSerialize property to omit no json
            property.ShouldSerialize = instance =>
                instance.GetType().GetProperty(property.PropertyName).GetValue(instance) != null;
        }
        return property;
    }
}

// Performs the conversion to and from a JSON value to compound type
public class OptionalConverter : JsonConverter
{
    public override bool CanWrite => true;
    public override bool CanRead => true;

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Optional<>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jtoken = JToken.Load(reader);
        var genericTypeArgument = objectType.GetGenericArguments()[0];
        var constructor = objectType.GetConstructor(new[] { genericTypeArgument });
        var result = JTokenType.Null != jtoken.Type ? jtoken.ToObject(genericTypeArgument) : null;

        return constructor.Invoke(new object[] { JTokenType.Null != jtoken.Type ? jtoken.ToObject(genericTypeArgument) : null });
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var val = value.GetType().GetProperty("Value").GetValue(value);
        (val != null ? JValue.FromObject(val) : JValue.CreateNull()).WriteTo(writer);
    }
}

功勞全歸@dbc。

void Main()
{
    var settings = new Settings();

    // no key {}
    settings.OptionalIntegerSetting = null;
    JsonConvert.SerializeObject(settings).Dump();

    // null key {\"OptionalIntegerSetting\" : null}
    settings.OptionalIntegerSetting = null;
    settings.OptionalIntegerSettingSpecified = true;
    JsonConvert.SerializeObject(settings).Dump();

    // has value {\"OptionalIntegerSetting\" : 123}
    settings.OptionalIntegerSetting = 123;
    JsonConvert.SerializeObject(settings).Dump();

    JsonConvert.DeserializeObject<Settings>("{}").Dump();
    JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : null}").Dump();
    JsonConvert.DeserializeObject<Settings>("{'OptionalIntegerSetting' : '123'}").Dump();
}

public class Settings
{
    public uint? OptionalIntegerSetting { get; set; }

    [JsonIgnore]
    public bool OptionalIntegerSettingSpecified { get; set;}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM