如何在.net core 3.1中使用System.Text.Json获取对象内部对象的值
How to get value of object inside object using System.Text.Json in .net core 3.1
创建了一个 .Net Core 3.1 Web 应用程序并发布请求,其中请求的模型看起来像,
public class RequestPayload
{
public string MessageName { get; set; }
public object Payload { get; set; }
}
我是 core 3.1 的新手,正在努力获取 Payload 的值 属性,谁能帮我解决这个问题?
在找到解决方案的同时,我还比较了 Newtonsoft 和 System.Text.Json 并得到 Error.
使用 Newtonsoft 我能够 序列化和反序列化如下所示的模型,
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我不是
虽然 序列化 出现错误 "System.Text.Json.JsonException: 'A possible object cycle was detected which is not supported."
为了测试 反序列化 ,以某种方式创建了 JSON 并尝试使用 System.Text.Json 对其进行反序列化,但出现错误 "System.Text.Json.JsonException: 'The JSON value could not be converted to System.Type. "
使用了 System.Text.Json.JsonSerializer,这是一个问题还是有其他可能使它起作用?
I am very new to core 3.1 and struggling to get the value of Payload property, Can anyone help me on this?
对于System.Object
属性,不像Newtonsoft.Json
,System.Text.Json
不会尝试推断JSON 原始值有效载荷的 type
(例如 true
、12345.67
、"hello"
)。同样,对于复杂的 JSON 值,如对象和数组(例如 {"Name":"hi"}
或 [1, 2, 3]
),对象 属性 被设置为表示传入 JSON。这类似于 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
并对其调用转换 API 以获取 .NET 值(例如 GetProperty(String)
和 GetInt32()
)。
以下示例显示了如何访问 Payload
值,一旦您将 JSON 反序列化为 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);
}
While finding the solution I also compared Newtonsoft and System.Text.Json and got Error.
尽管序列化包含 System.Type
属性 的 class 是可行的,但 不推荐 ,尤其是对于网络应用程序(虽然存在信息泄露的潜在问题)。
另一方面,反序列化 JSON 到包含 Type
属性 的 class,尤其是使用 Type.GetType(untrusted-string-input)
绝对不推荐,因为它会在您的应用程序中引入潜在的安全漏洞。
这就是内置System.Text.Json
故意不支持serializing/deserializingType
属性的原因。您在序列化时看到的异常消息是因为 Type
在其对象图中包含一个循环,而 JsonSerializer
当前不处理循环。如果您只关心将 class 序列化(即写入)到 JSON,您可以创建自己的 JsonConverter<Type>
来添加对它的支持(产生与 JSON 相同的 JSON =15=] 会)。像下面这样的东西会起作用:
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());
考虑重新评估 为什么您需要 Type
属性 在您的 class 上开始序列化和反序列化。
有关为什么不应使用 Type.GetType(string)
.
反序列化包含 Type
属性的 class 的更多信息和上下文,请参阅 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);
}
创建了一个 .Net Core 3.1 Web 应用程序并发布请求,其中请求的模型看起来像,
public class RequestPayload
{
public string MessageName { get; set; }
public object Payload { get; set; }
}
我是 core 3.1 的新手,正在努力获取 Payload 的值 属性,谁能帮我解决这个问题?
在找到解决方案的同时,我还比较了 Newtonsoft 和 System.Text.Json 并得到 Error.
使用 Newtonsoft 我能够 序列化和反序列化如下所示的模型,
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我不是 虽然 序列化 出现错误 "System.Text.Json.JsonException: 'A possible object cycle was detected which is not supported."
为了测试 反序列化 ,以某种方式创建了 JSON 并尝试使用 System.Text.Json 对其进行反序列化,但出现错误 "System.Text.Json.JsonException: 'The JSON value could not be converted to System.Type. "
使用了 System.Text.Json.JsonSerializer,这是一个问题还是有其他可能使它起作用?
I am very new to core 3.1 and struggling to get the value of Payload property, Can anyone help me on this?
对于System.Object
属性,不像Newtonsoft.Json
,System.Text.Json
不会尝试推断JSON 原始值有效载荷的 type
(例如 true
、12345.67
、"hello"
)。同样,对于复杂的 JSON 值,如对象和数组(例如 {"Name":"hi"}
或 [1, 2, 3]
),对象 属性 被设置为表示传入 JSON。这类似于 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
并对其调用转换 API 以获取 .NET 值(例如 GetProperty(String)
和 GetInt32()
)。
以下示例显示了如何访问 Payload
值,一旦您将 JSON 反序列化为 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);
}
While finding the solution I also compared Newtonsoft and System.Text.Json and got Error.
尽管序列化包含 System.Type
属性 的 class 是可行的,但 不推荐 ,尤其是对于网络应用程序(虽然存在信息泄露的潜在问题)。
另一方面,反序列化 JSON 到包含 Type
属性 的 class,尤其是使用 Type.GetType(untrusted-string-input)
绝对不推荐,因为它会在您的应用程序中引入潜在的安全漏洞。
这就是内置System.Text.Json
故意不支持serializing/deserializingType
属性的原因。您在序列化时看到的异常消息是因为 Type
在其对象图中包含一个循环,而 JsonSerializer
当前不处理循环。如果您只关心将 class 序列化(即写入)到 JSON,您可以创建自己的 JsonConverter<Type>
来添加对它的支持(产生与 JSON 相同的 JSON =15=] 会)。像下面这样的东西会起作用:
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());
考虑重新评估 为什么您需要 Type
属性 在您的 class 上开始序列化和反序列化。
有关为什么不应使用 Type.GetType(string)
.
Type
属性的 class 的更多信息和上下文,请参阅 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);
}