Я очень новичок в ядре 3.1 и изо всех сил пытаюсь получить значение свойства Payload. Может ли кто-нибудь помочь мне в этом?
Для System.Object
свойств, в отличие от Newtonsoft.Json
, System.Text.Json
не не пытается определить type
полезной нагрузки JSON для примитивных значений (таких как true
, 12345.67
, "hello"
). Аналогичным образом, для сложных JSON значений, таких как объекты и массивы (например, {"Name":"hi"}
или [1, 2, 3]
), свойство объекта устанавливается в рамке JsonElement
, которое представляет переданный 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
, вы можете просматривать и получать доступ к значениям в JSON объектной модели документов (DOM), используя 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);
}
При поиске решения я также сравнил Newtonsoft и System.Text. Json и получил ошибку.
Несмотря на то, что сериализация класса, содержащего свойство System.Type
, в порядке, она не рекомендуется, особенно для веб-приложений (хотя есть потенциальные проблемы с раскрытием информации).
С другой стороны, десериализация JSON в класс, который содержит свойство Type
, особенно использование Type.GetType(untrusted-string-input)
- определенно не рекомендуется , так как оно представляет потенциальные уязвимости безопасности в вашем приложении. * 10 53 *
Вот почему встроенные System.Text.Json
намеренно не поддерживают сериализацию / десериализацию Type
свойств. Сообщение об исключении, которое вы видите при сериализации, связано с тем, что Type
содержит цикл в графе объектов, а JsonSerializer
в настоящее время не обрабатывает циклы. Если вам нужно только сериализовать (то есть записать) класс в JSON, вы можете создать свой собственный JsonConverter<Type>
, чтобы добавить поддержку для него (для создания того же JSON, что и Newtonsoft.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());
Рассмотреть переоценку зачем вам нужно свойство Type
в вашем классе, который сериализуется и десериализуется для начала.
См. https://github.com/dotnet/corefx/issues/42712 для получения дополнительной информации и контекста, почему вы не должны ' • десериализовать классы, содержащие Type
свойства, используя Type.GetType(string)
.
. Вот дополнительная информация о том, как написать собственный конвертер: 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);
}