c# - Implementing ASP.NET Web API Optional Parameters -
i need ability distinguish between key not being supplied , null.
an example of json be:
# key not specified {} # key specified null {'optionalkey' : null} # key specified , valid {'optionalkey' : 123}
to distinguishable between key's absence , null, i've created generic optional class wraps each field, requires writing custom jsonconverter , defaultcontractresolver flatten json / unpack optionaltype (sending nested json each field not option).
i've managed create linqpad script can't thinking there must easier way doesn't involve reflection?
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 null assigns optional type class, not use 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' 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 way of populating poco resource model canserialise proerties @ point before serialisation. // prevents having define canserialisemyproperty method 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 additional shouldserialize property omit no json property.shouldserialize = instance => instance.gettype().getproperty(property.propertyname).getvalue(instance) != null; } return property; } } // performs conversion , json value 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); } }
full credit goes @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;} }
Comments
Post a Comment