デフォルトのコンストラクターと、パラメーターのセットを受け取るオーバーロードされたコンストラクターを持つクラスがあります。これらのパラメーターはオブジェクトのフィールドに一致し、構築時に割り当てられます。この時点で、他の目的のためにデフォルトのコンストラクタが必要なので、できればそれを保持したいと思います。
私の問題:デフォルトのコンストラクタを削除してJSON文字列を渡すと、オブジェクトは正しくデシリアライズされ、コンストラクタパラメータを問題なく渡します。私は、オブジェクトが期待どおりに設定された状態に戻ることになります。ただし、デフォルトのコンストラクターをオブジェクトに追加するとすぐに、JsonConvert.DeserializeObject<Result>(jsontext)
を呼び出すと、プロパティが設定されなくなります。
この時点で、逆シリアル化の呼び出しにnew JsonSerializerSettings(){CheckAdditionalContent = true}
を追加しようとしました。それは何もしませんでした。
別のメモ。コンストラクターのパラメーターは、パラメーターが小文字で始まることを除いて、フィールドの名前と正確に一致します。私が言ったように、デシリアライズはデフォルトのコンストラクタなしでうまく機能するので、これは重要ではないと思います。
これが私のコンストラクタのサンプルです:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Json.Netは、オブジェクトにデフォルトの(パラメーターのない)コンストラクターがある場合はそれを使用することを好みます。複数のコンストラクターがあり、Json.Netでデフォルト以外のコンストラクターを使用する場合は、Json.Netに呼び出すコンストラクターに[JsonConstructor]
属性を追加できます。
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
これが正しく機能するためには、コンストラクターのパラメーター名がJSONオブジェクトの対応するプロパティ名と一致する(大文字と小文字を区別しない)ことが重要です。ただし、必ずしもオブジェクトのプロパティごとにコンストラクターパラメーターを用意する必要はありません。コンストラクターパラメーターでカバーされないJSONオブジェクトプロパティの場合、Json.Netは、パブリックプロパティアクセサー(または[JsonProperty]
でマークされたプロパティ/フィールド)を使用して、オブジェクトの作成後にオブジェクトを設定しようとします。
クラスに属性を追加したくない場合や、逆シリアル化しようとしているクラスのソースコードを制御しない場合は、カスタム JsonConverter を作成して、オブジェクト。例えば:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
次に、コンバーターをシリアライザー設定に追加し、デシリアライズするときに設定を使用します。
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
ここでは少し遅れており、正確には適していませんが、 私の質問 はこのものの複製として閉じられていたため、このソリューションは完全に異なるため、ここに私のソリューションを追加します。
ユーザー定義のstruct型に最も具体的なコンストラクターを優先するようにJson.NET
に指示する一般的な方法が必要だったため、そのような各structが定義されているプロジェクトに依存関係を追加するJsonConstructor
属性を省略できます。
私は少しリバースエンジニアリングし、カスタムコントラクトリゾルバーを実装しました。そこで、CreateObjectContract
メソッドをオーバーライドして、カスタム作成ロジックを追加しました。
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
私はこのように使用しています。
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
Solution:
public Response Get(string jsonData) {
var json = JsonConvert.DeserializeObject<modelname>(jsonData);
var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
return data;
}
モデル:
public class modelname {
public long parameter{ get; set; }
public int parameter{ get; set; }
public int parameter{ get; set; }
public string parameter{ get; set; }
}
ここでの回答のいくつかに基づいて、現在のプロジェクトで使用するCustomConstructorResolver
を作成しました。
以下の解決メカニズムをサポートし、すべて構成可能です。
Newtonsoft.Json.JsonConstructorAttribute
を参照する必要があるため、Json.Netパッケージに依存しません。public class CustomConstructorResolver : DefaultContractResolver
{
public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
public bool IgnoreAttributeConstructor { get; set; } = false;
public bool IgnoreSinglePrivateConstructor { get; set; } = false;
public bool IgnoreMostSpecificConstructor { get; set; } = false;
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Use default contract for non-object types.
if (objectType.IsPrimitive || objectType.IsEnum) return contract;
// Look for constructor with attribute first, then single private, then most specific.
var overrideConstructor =
(this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType))
?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType))
?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));
// Set override constructor if found, otherwise use default contract.
if (overrideConstructor != null)
{
SetOverrideCreator(contract, overrideConstructor);
}
return contract;
}
private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
{
contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
contract.CreatorParameters.Clear();
foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
{
contract.CreatorParameters.Add(constructorParameter);
}
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();
if (constructors.Count == 1) return constructors[0];
if (constructors.Count > 1)
throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");
return null;
}
protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
return constructors.Length == 1 ? constructors[0] : null;
}
protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(e => e.GetParameters().Length);
var mostSpecific = constructors.LastOrDefault();
return mostSpecific;
}
}
以下に、XMLドキュメントをGistとして含む完全なバージョンを示します。 https://Gist.github.com/maverickelementalch/80f77f4b6bdce3b434b0f7a1d06baa95
フィードバックに感謝します。
Newtonsoft.Jsonのデフォルトの動作では、public
コンストラクターが検索されます。デフォルトのコンストラクターが含まれるクラスまたは同じアセンブリでのみ使用される場合、Newtonsoft.Jsonが目的のprotected
コンストラクターを選択するように、アクセスレベルをinternal
またはpublic
に減らすことができます。
確かに、このソリューションは特定のケースに限定されています。
internal Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}