Solución:
Su problema se puede reproducir con el siguiente ejemplo más mínimo. Defina el siguiente modelo:
public class JsonApiMessage
public JsonElement data get; set;
Luego intente deserializar y volver a serializar un objeto JSON vacío así:
var payload = JsonSerializer.Deserialize("");
var newJson = JsonSerializer.Serialize(payload, new JsonSerializerOptions WriteIndented = true );
Y obtendrá una excepción (demo violín n. ° 1 aquí):
System.InvalidOperationException: Operation is not valid due to the current state of the object.
at System.Text.Json.JsonElement.WriteTo(Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.JsonConverterJsonElement.Write(Utf8JsonWriter writer, JsonElement value, JsonSerializerOptions options)
El problema parece ser que JsonElement
es un struct
y el valor predeterminado de esta estructura no se puede serializar. De hecho, simplemente haciendo JsonSerializer.Serialize(new JsonElement());
lanza la misma excepción (demo violín # 2 aquí). (Esto contrasta con JObject
que es un tipo de referencia cuyo valor predeterminado es, por supuesto, null
.)
¿Entonces cuales son tus opciones? Podrías hacer todo tu JsonElement
las propiedades sean anulables y se establezcan IgnoreNullValues = true
mientras se vuelve a serializar:
public class JsonApiData
[JsonPropertyName("type")]
public string Type get; set;
[JsonPropertyName("id")]
public string Id get; set;
[JsonPropertyName("attributes")]
public JsonElement? Attributes get; set;
[JsonPropertyName("meta")]
public JsonElement? Meta get; set;
[JsonPropertyName("relationships")]
public JsonElement? Relationships get; set;
Y luego:
var reserialisedPayload = JsonSerializer.Serialize(payload, new JsonSerializerOptions IgnoreNullValues = true );
Demostración del violín n. ° 3 aquí.
O en .NET 5 o posterior, puedes marcar todos tus JsonElement
propiedades con [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
:
public class JsonApiData
// Remainder unchanged
[JsonPropertyName("attributes")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonElement Attributes get; set;
[JsonPropertyName("meta")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonElement Meta get; set;
[JsonPropertyName("relationships")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public JsonElement Relationships get; set;
Si lo hace, los elementos no inicializados se omitirán durante la serialización sin necesidad de modificar las opciones de serialización.
Demostración del violín n. ° 4 aquí.
O bien, puede simplificar su modelo de datos vinculando todas las propiedades JSON que no sean Id
a un JsonExtensionData
propiedad como tal:
public class JsonApiData
[JsonPropertyName("id")]
public string Id get; set;
[JsonExtensionData]
public Dictionary ExtensionData get; set;
Este enfoque evita la necesidad de configurar manualmente IgnoreNullValues
al volver a serializar y, por lo tanto, ASP.NET Core volverá a serializar el modelo correctamente de forma automática.
Demostración del violín n. ° 5 aquí.
La excepción es correcta: el estado del objeto no es válido. los Meta
y Relasionships
los elementos no aceptan valores NULL, pero el JSON string no los contiene. los Delawareel objeto serializado termina con Undefined
valores en esas propiedades que no se pueden serializar.
[JsonPropertyName("meta")]
public JsonElement? Meta get; set;
[JsonPropertyName("relationships")]
public JsonElement? Relationships get; set;
La solución rápida sería cambiar esas propiedades a JsonElement?
. Esto permitirá una deserialización y serialización correctas. De forma predeterminada, los elementos faltantes se emitirán como nulos:
"meta": null,
"relationships": null
Para ignorarlos, agregue el IgnoreNullValues =true
opción :
var newJson = JsonSerializer.Serialize(payload, new JsonSerializerOptions
WriteIndented = true,IgnoreNullValues =true );
los verdadero Sin embargo, la solución sería deshacerse de todo ese código. Eso cestas el uso de System.Text.Json. Dejado por sí mismo, ASP.NET Core usa Pipelines para leer el flujo de entrada sin asignar, deserializa la carga útil y llama al método con el objeto deserializado como parámetro, utilizando asignaciones mínimas. Todos los valores devueltos se serializan de la misma manera.
Sin embargo, el código de la pregunta asigna mucho: almacena en caché la entrada en StreamReader, luego toda la carga útil se almacena en caché en el payloadString
y luego otra vez, como el payload
objeto. El proceso inverso también utiliza cadenas temporales. Este código toma por lo menos el doble de RAM de la que usaría ASP.NET Core.
El código de acción debe ser solo:
[HttpPost("eventType")]
public async Task ProcessEventAsync([FromRoute] string eventType,
MyApiData payload)
Guid messageID = Guid.NewGuid();
payload.Data.Id = messageID.ToString();
return Accepted(payload);
Dónde MyApiData
es un objeto fuertemente tipado. La forma del ejemplo de Json corresponde a:
public class Attributes
public string source get; set;
public string instance get; set;
public string level get; set;
public string message get; set;
public class Data
public string type get; set;
public Attributes attributes get; set;
public class MyApiData
public Data data get; set;
public Data[] included get;set;
Todas las demás comprobaciones las realiza ASP.NET Core; ASP.NET Core rechazará cualquier POST
que no tiene el tipo MIME correcto. Devolverá un 400 si la solicitud está mal formateada. Devolverá un 500 si el código arroja
Nos puedes sustentar nuestra investigación poniendo un comentario y puntuándolo te estamos agradecidos.