カスタムモデルバインダーは必要ありません。また、リクエストパイプラインをいじくり回す必要もありません。
この他のSOを見てください:JSON.NETにカスタムJsonConverterを実装して、基本クラスオブジェクトのリストを逆シリアル化する方法は?。
私はこれを同じ問題に対する私自身の解決策の基礎として使用しました。
そのSOで参照されているものから始めますJsonCreationConverter<T>
(応答の型のシリアル化に関する問題を修正するために少し変更されています):
public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// this is very important, otherwise serialization breaks!
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">contents of JSON object that will be
/// deserialized</param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
JsonConverterAttribute
これで、Json.Netをカスタムコンバーターにポイントして、タイプに注釈を付けることができます。
[JsonConverter(typeof(MyCustomConverter))]
public abstract class BaseClass{
private class MyCustomConverter : JsonCreationConverter<BaseClass>
{
protected override BaseClass Create(Type objectType,
Newtonsoft.Json.Linq.JObject jObject)
{
//TODO: read the raw JSON object through jObject to identify the type
//e.g. here I'm reading a 'typename' property:
if("DerivedType".Equals(jObject.Value<string>("typename")))
{
return new DerivedClass();
}
return new DefaultClass();
//now the base class' code will populate the returned object.
}
}
}
public class DerivedClass : BaseClass {
public string DerivedProperty { get; set; }
}
public class DefaultClass : BaseClass {
public string DefaultProperty { get; set; }
}
これで、基本タイプをパラメーターとして使用できます。
public Result Post(BaseClass arg) {
}
そして、投稿する場合:
{ typename: 'DerivedType', DerivedProperty: 'hello' }
次にarg
、のインスタンスになりますが、DerivedClass
投稿した場合:
{ DefaultProperty: 'world' }
次に、のインスタンスを取得しますDefaultClass
。
編集-なぜ私はこの方法を好むのですかTypeNameHandling.Auto/All
TypeNameHandling.Auto/All
JotaBeが支持するものを使用することが常に理想的な解決策であるとは限らないと私は信じています。この場合はそうかもしれませんが、個人的には、次の場合を除いては行いません。
- 私のAPIは私または私のチームによってのみ使用されます
- デュアルXML互換エンドポイントを使用する必要はありません
Json.NetTypeNameHandling.Auto
またはAll
を使用すると、Webサーバーはタイプ名をの形式で送信し始めますMyNamespace.MyType, MyAssemblyName
。
私はコメントで、これはセキュリティ上の懸念だと思います。これについては、Microsoftから読んだいくつかのドキュメントで言及されています。それはもう言及されていないようですが、それでも私はそれが正当な懸念であると感じています。名前空間で修飾された型名とアセンブリ名を外部に公開したくありません。それは私の攻撃対象領域を増やしています。だから、はい、私はObject
自分のAPIタイプのプロパティ/パラメーターを持つことはできませんが、私のサイトの残りの部分は完全に穴がないと誰が言いますか?将来のエンドポイントがタイプ名を悪用する機能を公開しないと誰が言いますか?簡単だからといって、なぜそのチャンスをつかむのですか?
また、「適切な」APIを作成している場合、つまり、自分だけでなくサードパーティが使用するために、Web APIを使用している場合は、JSON/XMLコンテンツタイプを活用することを検討している可能性があります。処理(少なくとも)。XML形式とJSON形式ですべてのAPIタイプを異なる方法で参照する、使いやすいドキュメントをどこまで作成しようとしているのかを確認してください。
JSON.Netが型名をどのように理解するかをオーバーライドすることで、2つを並べて、型名がどちらか一方で覚えやすいという理由ではなく、純粋に好みに基づいて呼び出し元のXML/JSONを選択できます。