。Net Core 3.1 Webアプリケーションを作成し、リクエストされたモデルが次のようになるリクエストを投稿しました。
public class RequestPayload
{
public string MessageName { get; set; }
public object Payload { get; set; }
}
私はコア3.1に非常に慣れておらず、ペイロードプロパティの値を取得するのに苦労しています。これについて誰かが私を助けてくれますか?
解決策を見つけている間、私はNewtonsoftとSystem.Text.Jsonを比較してErrorも取得しました。
Newtonsoft私はableを使用して、以下に示すモデルをシリアライズおよびデシリアライズします、
public class RequestPayload
{
public string MessageName { get; set; }
public object Payload { get; set; }
//Problem is here -> TYPE
public Type PayloadType { get; set; }
}
しかしSystem.Text.Jsonを使用していますnot While serializingがエラーを取得しました "System.Text.Json.JsonException: '可能なオブジェクトサイクルはサポートされていないことが検出されました。」
deserializationをテストするために、何らかの方法でJSONを作成し、System.Text.Jsonを使用してそれを非直列化しようとしましたが、エラー「System.Text.Json.JsonException:」が発生しました。 」
使用System.Text.Json.JsonSerializer、それは問題ですか、またはこれを機能させる他の可能性がありますか?
私はコア3.1に非常に慣れておらず、ペイロードプロパティの値を取得するのに苦労しています。これについて誰かが私を助けてくれますか?
System.Object
プロパティの場合、unlikeNewtonsoft.Json
、System.Text.Json
はしないを推測しようとしますプリミティブ値のJSONペイロードのtype
(true
、12345.67
、"hello"
など)。同様に、オブジェクトや配列などの複雑なJSON値({"Name":"hi"}
や[1, 2, 3]
など)の場合、オブジェクトプロパティは、渡されたJSONを表すボックス化されたJsonElement
として設定されます。これは、Newtonsoft.Json
がJObject
をobject property
に格納して複雑な型を作成する方法と似ています。 https://docs.Microsoft.com/en-us/dotnet/api/system.text.json.jsonelement?view=netcore-3.1 を参照してください
Newtonsoft.JsonのJObject
の場合と同様に、JsonElement
を使用してJSONドキュメントオブジェクトモデル(DOM)内の値をトラバースおよびアクセスし、そのAPIを呼び出して.NET値( GetProperty(String)
やGetInt32()
など)。
次の例は、JSONをPayload
に逆シリアル化した後、RequestPayload
値にアクセスする方法を示しています。
private static void ObjectPropertyExample()
{
using JsonDocument doc = JsonDocument.Parse("{\"Name\":\"Darshana\"}");
JsonElement payload = doc.RootElement.Clone();
var requestPayload = new RequestPayload
{
MessageName = "message",
Payload = payload
};
string json = JsonSerializer.Serialize(requestPayload);
Console.WriteLine(json);
// {"MessageName":"message","Payload":{"Name":"Darshana"}}
RequestPayload roundtrip = JsonSerializer.Deserialize<RequestPayload>(json);
JsonElement element = (JsonElement)roundtrip.Payload;
string name = element.GetProperty("Name").GetString();
Assert.Equal("Darshana", name);
}
解決策を見つけるときに、NewtonsoftとSystem.Text.Jsonを比較してエラーが発生しました。
System.Type
プロパティを含むクラスをシリアル化することは問題ありませんが、特に推奨されていません、特にWebアプリケーションの場合(ただし、情報開示に潜在的な問題があります) 。
一方、deserializationJSONはType
プロパティを含むクラスに、特にType.GetType(untrusted-string-input)
を使用すると、は間違いなく推奨アプリケーションに潜在的なセキュリティの脆弱性をもたらすため。
これが、組み込みのSystem.Text.Json
が意図的にシリアル化/非シリアル化Type
プロパティをサポートしない理由です。シリアル化中に表示される例外メッセージは、Type
がオブジェクトグラフ内にサイクルを含み、JsonSerializer
が現在サイクルを処理していないためです。クラスをJSONにシリアル化(つまり、書き込み)するだけの場合は、独自のJsonConverter<Type>
を作成してサポートを追加できます(Newtonsoft.Json
と同じJSONを生成するため)。次のようなものが機能します。
private class CustomJsonConverterForType : JsonConverter<Type>
{
public override Type Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
// Caution: Deserialization of type instances like this
// is not recommended and should be avoided
// since it can lead to potential security issues.
// If you really want this supported (for instance if the JSON input is trusted):
// string assemblyQualifiedName = reader.GetString();
// return Type.GetType(assemblyQualifiedName);
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, Type value,
JsonSerializerOptions options)
{
// Use this with caution, since you are disclosing type information.
writer.WriteStringValue(value.AssemblyQualifiedName);
}
}
次に、カスタムコンバーターをオプションに追加し、それをJsonSerializer.Serialize
に渡します。
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomJsonConverterForType());
re-evaluatingなぜ最初にシリアル化および逆シリアル化されているクラスのType
プロパティが必要なのかを検討してください。
Type
プロパティを含むクラスをType.GetType(string)
を使用してデシリアライズしない理由の詳細とコンテキストについては、 https://github.com/dotnet/corefx/issues/42712 を参照してください。 。
カスタムコンバーターの作成方法の詳細は次のとおりです。 https://docs.Microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to
より安全に機能する方法(したがって、私がお勧めすること)は、予期してサポートし、明示的に作成する静的に既知の型のリストを含む型判別子列挙を使用することですJsonConverter<Type>
内の列挙値に基づくタイプ。
これがどのように見えるかの例です:
// Let's assume these are the list of types we expect for the `Type` property
public class ExpectedType1 { }
public class ExpectedType2 { }
public class ExpectedType3 { }
public class CustomJsonConverterForType : JsonConverter<Type>
{
public override Type Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
TypeDiscriminator typeDiscriminator = (TypeDiscriminator)reader.GetInt32();
Type type = typeDiscriminator switch
{
TypeDiscriminator.ExpectedType1 => typeof(ExpectedType1),
TypeDiscriminator.ExpectedType2 => typeof(ExpectedType2),
TypeDiscriminator.ExpectedType3 => typeof(ExpectedType3),
_ => throw new NotSupportedException(),
};
return type;
}
public override void Write(Utf8JsonWriter writer, Type value,
JsonSerializerOptions options)
{
if (value == typeof(ExpectedType1))
{
writer.WriteNumberValue((int)TypeDiscriminator.ExpectedType1);
}
else if (value == typeof(ExpectedType2))
{
writer.WriteNumberValue((int)TypeDiscriminator.ExpectedType2);
}
else if (value == typeof(ExpectedType3))
{
writer.WriteNumberValue((int)TypeDiscriminator.ExpectedType3);
}
else
{
throw new NotSupportedException();
}
}
// Used to map supported types to an integer and vice versa.
private enum TypeDiscriminator
{
ExpectedType1 = 1,
ExpectedType2 = 2,
ExpectedType3 = 3,
}
}
private static void TypeConverterExample()
{
var requestPayload = new RequestPayload
{
MessageName = "message",
Payload = "payload",
PayloadType = typeof(ExpectedType1)
};
var options = new JsonSerializerOptions()
{
Converters = { new CustomJsonConverterForType() }
};
string json = JsonSerializer.Serialize(requestPayload, options);
Console.WriteLine(json);
// {"MessageName":"message","Payload":"payload","PayloadType":1}
RequestPayload roundtrip = JsonSerializer.Deserialize<RequestPayload>(json, options);
Assert.Equal(typeof(ExpectedType1), roundtrip.PayloadType);
}