Migración desde Newtonsoft.Json a System.Text.Json
En este artículo se muestra cómo realizar la migración de Newtonsoft.Json a System.Text.Json.
El espacio de nombres System.Text.Json
proporciona funcionalidad para serializar y deserializar desde JSON (notaciones de objetos JavaScript). La biblioteca de System.Text.Json
se incluye en el runtime de .NET Core 3.1 y versiones posteriores. En cuanto a otros marcos de destino, instale el paquete NuGet System.Text.Json, que admite lo siguiente:
- .NET Standard 2.0 y versiones posteriores
- .NET Framework 4.6.2 y versiones posteriores
- .NET Core 2.0, 2.1 y 2.2
Sugerencia
Puede usar la asistencia de IA para migrar desde Newtonsoft.Json
con GitHub Copilot.
System.Text.Json
se centra principalmente en el rendimiento, la seguridad y el cumplimiento de estándares. Tiene algunas diferencias clave en el comportamiento predeterminado y no pretende tener paridad de características con Newtonsoft.Json
. En algunos escenarios, System.Text.Json
no tiene actualmente ninguna funcionalidad integrada, pero se recomiendan algunas soluciones alternativas. En otros escenarios, las soluciones alternativas no son prácticas.
El equipo de System.Text.Json
está invirtiendo en agregar las características que se solicitan con más frecuencia. Si su aplicación depende de una característica que falta, puede presentar una incidencia en el repositorio dotnet/runtime de GitHub para averiguar si se puede agregar compatibilidad para su escenario.
La mayor parte de este artículo trata sobre cómo usar la API de JsonSerializer, pero también incluye instrucciones sobre cómo usar JsonDocument (que representa los Document Object Model o DOM), Utf8JsonReader y Utf8JsonWriter.
En Visual Basic, no se puede usar Utf8JsonReader, lo que también significa que no se pueden escribir convertidores personalizados. La mayoría de las soluciones alternativas que se presentan aquí requieren que escriba convertidores personalizados. Puede escribir un convertidor personalizado en C# y registrarlo en un proyecto de Visual Basic. Para más información, consulte Soporte técnico de Visual Basic.
Tabla de diferencias
En la siguiente tabla se enumeran las características de Newtonsoft.Json
y las equivalentes de System.Text.Json
. Existen varias categorías de equivalentes:
- ✔️ Compatible con la funcionalidad integrada. Para obtener un comportamiento similar al de
System.Text.Json
es posible que se requiera el uso de un atributo o una opción global. - ⚠️ No compatible; hay disponible una solución alternativa. Las soluciones alternativas son convertidores personalizados, que puede que no proporcionen una paridad completa con la funcionalidad
Newtonsoft.Json
. En esos casos, se proporciona un código de ejemplo como muestra. Si confía en estas características deNewtonsoft.Json
, la migración requerirá modificaciones en los modelos de objetos .NET u otros cambios de código. - ❌ No compatible; la solución alternativa no es práctica o no es posible. Si confía en estas características de
Newtonsoft.Json
, no será posible realizar la migración sin cambios importantes.
Característica: Newtonsoft.Json | Equivalente: System.Text.Json |
---|---|
Deserialización sin distinción entre mayúsculas y minúsculas de forma predeterminada | ✔️ Valor global PropertyNameCaseInsensitive |
Nombres de propiedad en mayúsculas y minúsculas (Camel) | ✔️ Valor global PropertyNamingPolicy |
Nombres de propiedad con palabras unidas por guiones bajos | ✔️ Directiva de nomenclatura con palabras unidas por guiones bajos y en minúsculas (snake case) |
Mínimo escape de caracteres | ✔️ Escape de caracteres estricto, configurable |
Valor global NullValueHandling.Ignore |
✔️ Valor global DefaultIgnoreCondition |
Permitir comentarios | ✔️ Valor global ReadCommentHandling |
Permitir comas finales | ✔️ Valor global AllowTrailingCommas |
Registro del convertidor personalizado | ✔️ El orden de precedencia es diferente |
Profundidad máxima predeterminada 64, configurable | ✔️ Profundidad máxima predeterminada de 64, configurable |
Valor global PreserveReferencesHandling |
✔️ Valor global ReferenceHandling |
Serializar o deserializar números entre comillas | ✔️ Valor global de NumberHandling, atributo [JsonNumberHandling] |
Deserialización en clases y estructuras inmutables | ✔️ JsonConstructor, registros C# 9 |
Compatibilidad con campos | ✔️ Valor global IncludeFields, atributo [JsonInclude] |
Valor global DefaultValueHandling |
✔️ Valor global DefaultIgnoreCondition |
Valor NullValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Valor DefaultValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Deserializar Dictionary con clave que no sea de cadena |
✔️ Admitido |
Compatibilidad con captadores y establecedores de propiedad no públicos | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Valor global ReferenceLoopHandling |
✔️ Valor global ReferenceHandling |
Devoluciones de llamada | ✔️ Devoluciones de llamada |
NaN, Infinity, -Infinity | ✔️ Admitido |
Valor Required en el atributo [JsonProperty] |
✔️ Atributo [JsonRequired] y modificador obligatorio de C# |
DefaultContractResolver para omitir las propiedades |
✔️ Clase DefaultJsonTypeInfoResolver |
Serialización polimórfica | ✔️ Atributo [JsonDerivedType] |
Deserialización polimórfica | ✔️ Discriminador de tipos en el atributo [JsonDerivedType] |
Deserializar valor de enumeración de cadena | ✔️ Deserializar valores de enumeración de cadena |
Valor global MissingMemberHandling |
✔️ Control de los miembros que faltan |
Rellenar las propiedades sin establecedores | ✔️ Rellenar las propiedades sin establecedores |
Valor global ObjectCreationHandling |
✔️ Reutilizar en lugar de reemplazar las propiedades |
Compatibilidad con una gran variedad de tipos | ⚠ Algunos tipos requieren convertidores personalizados |
Deserializar los tipos inferidos en propiedades de object |
⚠No compatibles, solución alternativa, ejemplo |
Deserializar el literal null de JSON a tipos de valor que no aceptan valores NULL |
⚠No compatibles, solución alternativa, ejemplo |
Valores DateTimeZoneHandling y DateFormatString |
⚠No compatibles, solución alternativa, ejemplo |
Método JsonConvert.PopulateObject |
⚠No compatible, solución alternativa |
Compatibilidad con atributos System.Runtime.Serialization |
⚠No compatibles, solución alternativa, ejemplo |
JsonObjectAttribute |
⚠No compatible, solución alternativa |
Permitir nombres de propiedad sin comillas | ❌No compatible por diseño |
Permitir comillas simples alrededor de los valores de cadena | ❌No compatible por diseño |
Permitir valores JSON que no son de cadena para las propiedades de cadena | ❌No compatible por diseño |
Valor global TypeNameHandling.All |
❌No compatible por diseño |
Soporte técnico para consultas JsonPath |
❌No compatible |
Límites configurables | ❌No compatible |
Característica: Newtonsoft.Json | Equivalente: System.Text.Json |
---|---|
Deserialización sin distinción entre mayúsculas y minúsculas de forma predeterminada | ✔️ Valor global PropertyNameCaseInsensitive |
Nombres de propiedad en mayúsculas y minúsculas (Camel) | ✔️ Valor global PropertyNamingPolicy |
Mínimo escape de caracteres | ✔️ Escape de caracteres estricto, configurable |
Valor global NullValueHandling.Ignore |
✔️ Valor global DefaultIgnoreCondition |
Permitir comentarios | ✔️ Valor global ReadCommentHandling |
Permitir comas finales | ✔️ Valor global AllowTrailingCommas |
Registro del convertidor personalizado | ✔️ El orden de precedencia es diferente |
Profundidad máxima predeterminada 64, configurable | ✔️ Profundidad máxima predeterminada de 64, configurable |
Valor global PreserveReferencesHandling |
✔️ Valor global ReferenceHandling |
Serializar o deserializar números entre comillas | ✔️ Valor global de NumberHandling, atributo [JsonNumberHandling] |
Deserialización en clases y estructuras inmutables | ✔️ JsonConstructor, registros C# 9 |
Compatibilidad con campos | ✔️ Valor global IncludeFields, atributo [JsonInclude] |
Valor global DefaultValueHandling |
✔️ Valor global DefaultIgnoreCondition |
Valor NullValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Valor DefaultValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Deserializar Dictionary con clave que no sea de cadena |
✔️ Admitido |
Compatibilidad con captadores y establecedores de propiedad no públicos | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Valor global ReferenceLoopHandling |
✔️ Valor global ReferenceHandling |
Devoluciones de llamada | ✔️ Devoluciones de llamada |
NaN, Infinity, -Infinity | ✔️ Admitido |
Valor Required en el atributo [JsonProperty] |
✔️ Atributo [JsonRequired] y modificador obligatorio de C# |
DefaultContractResolver para omitir las propiedades |
✔️ Clase DefaultJsonTypeInfoResolver |
Serialización polimórfica | ✔️ Atributo [JsonDerivedType] |
Deserialización polimórfica | ✔️ Discriminador de tipos en el atributo [JsonDerivedType] |
Deserializar valor de enumeración de cadena | ✔️ Deserializar valores de enumeración de cadena |
Compatibilidad con una gran variedad de tipos | ⚠ Algunos tipos requieren convertidores personalizados |
Deserializar los tipos inferidos en propiedades de object |
⚠No compatibles, solución alternativa, ejemplo |
Deserializar el literal null de JSON a tipos de valor que no aceptan valores NULL |
⚠No compatibles, solución alternativa, ejemplo |
Valores DateTimeZoneHandling y DateFormatString |
⚠No compatibles, solución alternativa, ejemplo |
Método JsonConvert.PopulateObject |
⚠No compatible, solución alternativa |
Valor global ObjectCreationHandling |
⚠No compatible, solución alternativa |
Agregar a colecciones sin establecedores | ⚠No compatible, solución alternativa |
Nombres de propiedad con palabras unidas por guiones bajos | ⚠No compatible, solución alternativa |
Compatibilidad con atributos System.Runtime.Serialization |
⚠No compatibles, solución alternativa, ejemplo |
Valor global MissingMemberHandling |
⚠No compatibles, solución alternativa, ejemplo |
JsonObjectAttribute |
⚠No compatible, solución alternativa |
Permitir nombres de propiedad sin comillas | ❌No compatible por diseño |
Permitir comillas simples alrededor de los valores de cadena | ❌No compatible por diseño |
Permitir valores JSON que no son de cadena para las propiedades de cadena | ❌No compatible por diseño |
Valor global TypeNameHandling.All |
❌No compatible por diseño |
Soporte técnico para consultas JsonPath |
❌No compatible |
Límites configurables | ❌No compatible |
Característica: Newtonsoft.Json | Equivalente: System.Text.Json |
---|---|
Deserialización sin distinción entre mayúsculas y minúsculas de forma predeterminada | ✔️ Valor global PropertyNameCaseInsensitive |
Nombres de propiedad en mayúsculas y minúsculas (Camel) | ✔️ Valor global PropertyNamingPolicy |
Mínimo escape de caracteres | ✔️ Escape de caracteres estricto, configurable |
Valor global NullValueHandling.Ignore |
✔️ Valor global DefaultIgnoreCondition |
Permitir comentarios | ✔️ Valor global ReadCommentHandling |
Permitir comas finales | ✔️ Valor global AllowTrailingCommas |
Registro del convertidor personalizado | ✔️ El orden de precedencia es diferente |
Profundidad máxima predeterminada 64, configurable | ✔️ Profundidad máxima predeterminada de 64, configurable |
Valor global PreserveReferencesHandling |
✔️ Valor global ReferenceHandling |
Serializar o deserializar números entre comillas | ✔️ Valor global de NumberHandling, atributo [JsonNumberHandling] |
Deserialización en clases y estructuras inmutables | ✔️ JsonConstructor, registros C# 9 |
Compatibilidad con campos | ✔️ Valor global IncludeFields, atributo [JsonInclude] |
Valor global DefaultValueHandling |
✔️ Valor global DefaultIgnoreCondition |
Valor NullValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Valor DefaultValueHandling en [JsonProperty] |
✔️ Atributo JsonIgnore |
Deserializar Dictionary con clave que no sea de cadena |
✔️ Admitido |
Compatibilidad con captadores y establecedores de propiedad no públicos | ✔️ Atributo JsonInclude |
Atributo [JsonConstructor] |
✔️ Atributo [JsonConstructor] |
Valor global ReferenceLoopHandling |
✔️ Valor global ReferenceHandling |
Devoluciones de llamada | ✔️ Devoluciones de llamada |
NaN, Infinity, -Infinity | ✔️ Admitido |
Deserializar valor de enumeración de cadena | ✔️ Deserializar valores de enumeración de cadena |
Compatibilidad con una gran variedad de tipos | ⚠ Algunos tipos requieren convertidores personalizados |
Serialización polimórfica | ⚠No compatibles, solución alternativa, ejemplo |
Deserialización polimórfica | ⚠No compatibles, solución alternativa, ejemplo |
Deserializar los tipos inferidos en propiedades de object |
⚠No compatibles, solución alternativa, ejemplo |
Deserializar el literal null de JSON a tipos de valor que no aceptan valores NULL |
⚠No compatibles, solución alternativa, ejemplo |
Valor Required en el atributo [JsonProperty] |
⚠No compatibles, solución alternativa, ejemplo |
DefaultContractResolver para omitir las propiedades |
⚠No compatibles, solución alternativa, ejemplo |
Valores DateTimeZoneHandling y DateFormatString |
⚠No compatibles, solución alternativa, ejemplo |
Método JsonConvert.PopulateObject |
⚠No compatible, solución alternativa |
Valor global ObjectCreationHandling |
⚠No compatible, solución alternativa |
Agregar a colecciones sin establecedores | ⚠No compatible, solución alternativa |
Nombres de propiedad con palabras unidas por guiones bajos | ⚠No compatible, solución alternativa |
JsonObjectAttribute |
⚠No compatible, solución alternativa |
Compatibilidad con atributos System.Runtime.Serialization |
❌No compatible |
Valor global MissingMemberHandling |
❌No compatible |
Permitir nombres de propiedad sin comillas | ❌No compatible por diseño |
Permitir comillas simples alrededor de los valores de cadena | ❌No compatible por diseño |
Permitir valores JSON que no son de cadena para las propiedades de cadena | ❌No compatible por diseño |
Valor global TypeNameHandling.All |
❌No compatible por diseño |
Soporte técnico para consultas JsonPath |
❌No compatible |
Límites configurables | ❌No compatible |
Esta no es una lista exhaustiva de características de Newtonsoft.Json
. La lista incluye muchos de los escenarios que se han solicitado en publicaciones de problemas de GitHub o StackOverflow. Si implementa una solución alternativa para uno de los escenarios que aquí se enumeran que no tenga actualmente un código de ejemplo, y si quiere compartir la solución, haga clic en Esta página en la sección Comentarios de la parte inferior de esta página. De esta forma se abre una incidencia en el repositorio de GitHub de esta documentación y también se muestra en la sección Comentarios de esta página.
Diferencias en el comportamiento predeterminado
System.Text.Json es estricto de forma predeterminada y evita cualquier conjetura o interpretación en nombre del llamador, con lo que resalta el comportamiento determinista. La biblioteca se ha diseñado intencionadamente de esta manera por motivos de rendimiento y seguridad. De manera predeterminada, Newtonsoft.Json
es flexible. Esta diferencia fundamental en el diseño es la responsable de muchas de las siguientes diferencias específicas en el comportamiento predeterminado.
Deserialización sin distinción entre mayúsculas y minúsculas
Durante la deserialización, Newtonsoft.Json
realiza de forma predeterminada la coincidencia de nombres de propiedad sin distinción entre mayúsculas y minúsculas. El valor predeterminado de System.Text.Json distingue entre mayúsculas y minúsculas, lo que proporciona un mejor rendimiento, ya que realiza una coincidencia exacta. Para obtener información sobre cómo realizar la coincidencia sin distinción entre mayúsculas y minúsculas, vea Coincidencia de propiedades sin distinción entre mayúsculas y minúsculas.
Si usa System.Text.Json
indirectamente mediante ASP.NET Core, no es necesario hacer nada para obtener un comportamiento como Newtonsoft.Json
. ASP.NET Core especifica los valores para los nombres de propiedad con palabras con las letras iniciales en mayúsculas (camel case) y la coincidencia sin distinción entre mayúsculas y minúsculas cuando se usa System.Text.Json
.
ASP.NET Core también permite deserializar números entrecomillados de forma predeterminada.
Mínimo escape de caracteres
Durante la serialización, Newtonsoft.Json
es relativamente permisivo con respecto a si se permite que los caracteres queden sin escapar. Es decir, no los reemplaza por \uxxxx
donde xxxx
es el punto de código del carácter. En aquellos casos donde los escapa, lo hace emitiendo una \
antes del carácter (por ejemplo, "
se convierte en \"
). De forma predeterminada, System.Text.Json escapa más caracteres para proporcionar protecciones de defensa en profundidad contra los ataques de scripting entre sitios (XSS) o de divulgación de información, y lo hace mediante el uso de la secuencia de seis caracteres. System.Text.Json
escapa todos los caracteres que no sean ASCII de forma predeterminada, por lo que no es necesario hacer nada si usa StringEscapeHandling.EscapeNonAscii
en Newtonsoft.Json
. System.Text.Json
de forma predeterminada también escapa los caracteres que distinguen HTML. Para obtener información sobre cómo invalidar el comportamiento predeterminado de System.Text.Json
, vea Personalización de la codificación de caracteres.
Comentarios
Durante la deserialización, Newtonsoft.Json
omite de forma predeterminada los comentarios en JSON. El valor predeterminado de System.Text.Json es producir excepciones para los comentarios porque la especificación RFC 8259 no los incluye. Para obtener información sobre cómo permitir comentarios, vea Permitir comentarios y comas finales.
Comas finales
Durante la deserialización, Newtonsoft.Json
omite de forma predeterminada las comas finales. También omite varias comas finales (por ejemplo, [{"Color":"Red"},{"Color":"Green"},,]
). El valor predeterminado de System.Text.Json es producir excepciones para las comas finales porque la especificación RFC 8259 no las permite. Para obtener información sobre cómo hacer que System.Text.Json
las acepte, vea Permitir comentarios y comas finales. No hay ninguna manera de permitir varias comas finales.
Precedencia de registro del convertidor
La precedencia de registro de Newtonsoft.Json
para los convertidores personalizados es la siguiente:
- Atributo en la propiedad
- Atributo en el tipo
- Colección de convertidores
Este orden implica que un convertidor que se registre aplicando un atributo en el nivel de tipo invalidará a un convertidor personalizado de la colección Converters
, y un atributo en el nivel de propiedad invalidará a ambos registros.
La precedencia de registro de System.Text.Json para los convertidores personalizados es diferente:
- Atributo en la propiedad
- Colección Converters
- Atributo en el tipo
En este caso, la diferencia es que un convertidor personalizado de la colección Converters
invalida a un atributo en el nivel de tipo. La intención de este orden de precedencia es que los cambios de tiempo de ejecución invaliden las opciones de tiempo de diseño. No hay ninguna manera de cambiar la precedencia.
Para más información sobre el registro de convertidores personalizados, vea Registro de un convertidor personalizado.
Profundidad máxima
La versión más reciente de Newtonsoft.Json
tiene un límite máximo de profundidad de 64 de forma predeterminada. System.Text.Json también tiene un límite predeterminado de 64, y se puede configurar mediante el valor JsonSerializerOptions.MaxDepth.
Si usa System.Text.Json
indirectamente mediante ASP.NET Core, el límite predeterminado de profundidad máxima es de 32. El valor predeterminado es el mismo que para el enlace de modelos y se establece en la clase JsonOptions.
Cadenas JSON (nombres de propiedad y valores de cadena)
Durante la deserialización, Newtonsoft.Json
acepta nombres de propiedad entre comillas dobles, comillas simples o sin comillas. Acepta valores de cadena entre comillas dobles o comillas simples. Por ejemplo, Newtonsoft.Json
acepta el siguiente código JSON:
{
"name1": "value",
'name2': "value",
name3: 'value'
}
System.Text.Json
solo acepta nombres de propiedad y valores de cadena entre comillas dobles, ya que ese es el formato requerido por la especificación RFC 8259 y es el único formato que se considera JSON válido.
Un valor entre comillas simples da como resultado una JsonException con el siguiente mensaje:
''' is an invalid start of a value.
Valores que no son de cadena para propiedades de cadena
Newtonsoft.Json
acepta valores que no son de cadena, como un número o los literales true
y false
para la deserialización de las propiedades de tipo cadena. A continuación se muestra un ejemplo de JSON que deserializa correctamente Newtonsoft.Json
en la clase siguiente:
{
"String1": 1,
"String2": true,
"String3": false
}
public class ExampleClass
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}
System.Text.Json
no deserializa valores que no son de cadena en propiedades de cadena. Un valor que no sea de cadena recibido para un campo de cadena da como resultado una JsonException con el siguiente mensaje:
The JSON value could not be converted to System.String.
Escenarios con JsonSerializer
Algunos de los escenarios siguientes no son compatibles con la funcionalidad integrada, pero hay disponibles soluciones alternativas. Las soluciones alternativas son convertidores personalizados, que puede que no proporcionen una paridad completa con la funcionalidad Newtonsoft.Json
. En esos casos, se proporciona un código de ejemplo como muestra. Si confía en estas características de Newtonsoft.Json
, la migración requerirá modificaciones en los modelos de objetos .NET u otros cambios de código.
Para algunos de los escenarios siguientes, no hay ninguna solución alternativa que sea práctica o posible. Si confía en estas características de Newtonsoft.Json
, no será posible realizar la migración sin cambios importantes.
Permitir o escribir números entre comillas
Newtonsoft.Json
puede serializar o deserializar los números representados por cadenas JSON (entre comillas). Por ejemplo, puede aceptar: {"DegreesCelsius":"23"}
en lugar de {"DegreesCelsius":23}
. Para habilitar ese comportamiento en System.Text.Json, establezca JsonSerializerOptions.NumberHandling en WriteAsString o AllowReadingFromString, o use el atributo [JsonNumberHandling].
Si usa System.Text.Json
indirectamente mediante ASP.NET Core, no es necesario hacer nada para obtener un comportamiento como Newtonsoft.Json
. ASP.NET Core especifica valores predeterminados web cuando utiliza System.Text.Json
y los valores predeterminados web permiten números entre comillas.
Para obtener más información, vea Permitir o escribir números entre comillas.
Especificación del constructor que se va a usar al deserializar
El atributo [JsonConstructor]
de Newtonsoft.Json
le permite especificar el constructor al que se llamará al deserializar en un objeto POCO.
System.Text.Json
también tiene un atributo [JsonConstructor]. Para obtener más información, vea Tipos y registros inmutables.
Omitir condicionalmente una propiedad
Newtonsoft.Json
ofrece varias formas de omitir condicionalmente una propiedad en la serialización o deserialización:
DefaultContractResolver
le permite seleccionar las propiedades que se van a incluir u omitir, en función de criterios arbitrarios.- Los valores
NullValueHandling
yDefaultValueHandling
deJsonSerializerSettings
le permiten especificar que se deben omitir todas las propiedades de valores NULL o valores predeterminados. - Los valores
NullValueHandling
yDefaultValueHandling
del atributo[JsonProperty]
le permiten especificar propiedades individuales que se deben omitir cuando se establecen en null o en el valor predeterminado.
System.Text.Json proporciona las siguientes formas de omitir propiedades o campos durante la serialización:
- El atributo [JsonIgnore] de una propiedad hace que la propiedad se omita en el objeto JSON durante la serialización.
- La opción global IgnoreReadOnlyProperties le permite omitir todas las propiedades de solo lectura.
- Si incluidos los campos, la opción global JsonSerializerOptions.IgnoreReadOnlyFields permite omitir todos los campos de solo lectura.
- La opción global
DefaultIgnoreCondition
le permite omitir todas las propiedades de tipo de valor que tienen valores predeterminados u omitir todas las propiedades de tipo de referencia que tengan valores NULL.
Además, en .NET 7 y versiones posteriores, puede personalizar el contrato JSON para omitir las propiedades en función de criterios arbitrarios. Para más información, consulte Contratos personalizados.
Estas opciones no permiten omitir las propiedades seleccionadas en función de criterios arbitrarios evaluados en tiempo de ejecución.
Campos públicos y no públicos
Newtonsoft.Json
puede serializar y deserializar los campos, así como las propiedades.
En System.Text.Json, use el valor global JsonSerializerOptions.IncludeFields o el atributo [JsonInclude] para incluir los campos públicos al serializar o deserializar. Para obtener un ejemplo, vea Inclusión de campos.
Conservación de referencias a objetos y procesado de bucles
De forma predeterminada, Newtonsoft.Json
realiza la serialización por valor. Por ejemplo, si un objeto contiene dos propiedades que contienen una referencia al mismo objeto Person
, los valores de las propiedades de dicho objeto Person
se duplican en JSON.
Newtonsoft.Json
tiene un valor PreserveReferencesHandling
en JsonSerializerSettings
que le permite realizar serializaciones por referencia:
- Los metadatos de un identificador se agregan al JSON creado para el primer objeto
Person
. - El JSON que se crea para el segundo objeto
Person
contiene una referencia a ese identificador en lugar de los valores de propiedad.
Newtonsoft.Json
también tiene un valor ReferenceLoopHandling
que le permite omitir las referencias circulares en lugar de producir una excepción.
Para conservar las referencias y administrar las referencias circulares en System.Text.Json, establezca JsonSerializerOptions.ReferenceHandler en Preserve. El valor ReferenceHandler.Preserve
es equivalente a PreserveReferencesHandling
= PreserveReferencesHandling.All
en Newtonsoft.Json
.
La opción ReferenceHandler.IgnoreCycles
tiene un comportamiento similar a Newtonsoft.JsonReferenceLoopHandling.Ignore
. Una diferencia es que la implementación System.Text.Json reemplaza los bucles de referencia por el token JSON null
en lugar de omitir la referencia de objeto. Para obtener más información, vea Omisión de las referencias circulares.
Al igual que Newtonsoft.JsonReferenceResolver, la clase System.Text.Json.Serialization.ReferenceResolver define el comportamiento de conservar las referencias en la serialización y deserialización. Cree una clase derivada para especificar el comportamiento personalizado. Para obtener un ejemplo, vea GuidReferenceResolver.
No se admiten algunas características de Newtonsoft.Json
relacionadas:
Para más información, consulte Conservación de las referencias y administración de las referencias circulares.
Diccionario con clave que no es de cadena
Tanto Newtonsoft.Json
como System.Text.Json
admiten colecciones de tipo Dictionary<TKey, TValue>
. Sin embargo, en System.Text.Json
, TKey
debe ser un tipo primitivo, no un tipo personalizado. Para más información, consulte Tipos de clave admitidos.
Precaución
Deserializar en un Dictionary<TKey, TValue>
en el que TKey
se escribe como cualquier otro elemento distinto de string
podría producir una vulnerabilidad de seguridad en la aplicación que consume. Para más información, consulte la incidencia 4761 de dotnet/runtime.
Tipos sin compatibilidad integrada
System.Text.Json no proporciona compatibilidad integrada con los siguientes tipos:
- DataTable y tipos relacionados (para más información, consulte Tipos de colecciones admitidos)
- ExpandoObject
- TimeZoneInfo
- BigInteger
- DBNull
- Type
- ValueTuple y sus tipos genéricos asociados
Se pueden implementar convertidores personalizados para tipos que no tienen compatibilidad integrada.
Serialización polimórfica
Newtonsoft.Json
realiza automáticamente la serialización polimórfica. A partir de .NET 7, System.Text.Json admite la serialización polimórfica mediante el atributo JsonDerivedTypeAttribute. Para más información, consulte Serialización de propiedades de clases derivadas.
Deserialización polimórfica
Newtonsoft.Json
tiene un valor TypeNameHandling
que agrega metadatos de nombre de tipo al código JSON durante la serialización. Usa los metadatos durante la deserialización para realizar la deserialización polimórfica. A partir de .NET 7, System.Text.Json se basa en la información del discriminador de tipos para realizar la deserialización polimórfica. Estos metadatos se emiten en el código JSON y, luego, se usan durante la deserialización para determinar si se van a deserializar en el tipo base o en un tipo derivado. Para más información, consulte Serialización de propiedades de clases derivadas.
Para admitir la deserialización polimórfica en versiones anteriores de .NET, cree un convertidor como en el ejemplo de Procedimiento para escribir convertidores personalizados.
Deserializar valores de enumeración de cadena
De forma predeterminada, System.Text.Json no admite la deserialización de valores de enumeración de cadena, mientras que Newtonsoft.Json
sí lo hace. Por ejemplo, el código siguiente genera una excepción JsonException:
string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.
class MyObj
{
public string Text { get; set; } = "";
public MyEnum Enum { get; set; }
}
enum MyEnum
{
One,
Two,
Three
}
Sin embargo, puede habilitar la deserialización de valores de enumeración de cadena mediante el convertidor JsonStringEnumConverter. Para más información, consulte Enumeraciones como cadenas.
Deserialización de propiedades de objeto
Cuando Newtonsoft.Json
deserializa en Object:
- infiere el tipo de valores primitivos en la carga JSON (excepto
null
) y devuelve los valoresstring
,long
,double
,boolean
oDateTime
almacenados como un objeto al que se ha aplicado la conversión boxing. Los valores primitivos son valores JSON únicos, como un número JSON, una cadena, un valortrue
,false
onull
. - Devuelve
JObject
oJArray
para valores complejos en la carga de JSON. Los valores complejos son colecciones de pares clave-valor JSON entre llaves ({}
) o listas de valores entre corchetes ([]
). Las propiedades y los valores entre llaves o corchetes pueden tener propiedades o valores adicionales. - Devuelve una referencia nula cuando la carga útil tiene el literal JSON
null
.
System.Text.Json almacena un objeto JsonElement
al que se ha aplicado la conversión boxing para valores primitivos y los complejos, siempre que se deserialice en Object; por ejemplo:
- Propiedad
object
. - Un valor de diccionario
object
. - Un valor de matriz
object
. - Una raíz
object
.
Pero System.Text.Json
trata null
igual que Newtonsoft.Json
, y devuelve una referencia nula cuando la carga útil tiene el literal JSON null
en ella.
Para implementar la inferencia de tipos para las propiedades object
, cree un convertidor como el ejemplo de Cómo escribir convertidores personalizados.
Deserialización de null en un tipo que no acepta valores NULL
Newtonsoft.Json
no provoca una excepción en el escenario siguiente:
NullValueHandling
se establece enIgnore
y,- durante la deserialización, el archivo JSON contiene un valor NULL para un tipo de valor que no acepta valores NULL.
En el mismo escenario, System.Text.Json produce una excepción. (La configuración de control de valores NULL correspondiente en System.Text.Json
es JsonSerializerOptions.IgnoreNullValues = true
).
Si es el propietario del tipo de destino, la mejor solución alternativa posible es hacer que la propiedad en cuestión acepte valores NULL (por ejemplo, cambiar int
a int?
).
Otra solución alternativa consiste en crear un convertidor para el tipo, como en el ejemplo siguiente, en el que se tratan los valores NULL de los tipos DateTimeOffset
:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
reader.TokenType == JsonTokenType.Null
? default
: reader.GetDateTimeOffset();
public override void Write(
Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue);
}
}
Registre este convertidor personalizado usando un atributo en la propiedad o agregando el convertidor a la colección Converters.
Nota: El convertidor anterior trata los valores NULL de manera diferente de como lo hace Newtonsoft.Json
para los POCO que especifican valores predeterminados. Por ejemplo, suponga que el siguiente código representa su objeto de destino:
public class WeatherForecastWithDefault
{
public WeatherForecastWithDefault()
{
Date = DateTimeOffset.Parse("2001-01-01");
Summary = "No summary";
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
Y suponga que el siguiente código JSON se deserializa con el convertidor anterior:
{
"Date": null,
"TemperatureCelsius": 25,
"Summary": null
}
Después de la deserialización, la propiedad Date
tiene 1/1/0001 (default(DateTimeOffset)
), es decir, se sobrescribe el valor establecido en el constructor. Dados los mismos objetos POCO y JSON, la deserialización de Newtonsoft.Json
dejaría 1/1/2001 en la propiedad Date
.
Deserialización en clases y estructuras inmutables
Newtonsoft.Json
se puede deserializar en clases y estructuras inmutables, ya que puede usar constructores que tengan parámetros.
En System.Text.Json, use el atributo [JsonConstructor] para especificar el uso de un constructor con parámetros. Los registros en C# 9 también son inmutables y se admiten como destinos de deserialización. Para obtener más información, vea Tipos y registros inmutables.
Propiedades obligatorias
En Newtonsoft.Json
, especifica que se requiere una propiedad estableciendo Required
en el atributo [JsonProperty]
. Newtonsoft.Json
produce una excepción si no se recibe ningún valor en el objeto JSON para una propiedad marcada como requerida.
A partir de .NET 7, puede usar el modificador required
de C# o el atributo JsonRequiredAttribute en una propiedad necesaria. System.Text.Json produce una excepción si la carga JSON no contiene un valor para la propiedad marcada. Para más información, consulte Propiedades necesarias.
System.Text.Json no produce una excepción si no se recibe ningún valor para una de las propiedades del tipo de destino. Por ejemplo, si tiene una clase WeatherForecast
:
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
El siguiente JSON se deserializa sin errores:
{
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Para que se produzca un error en la deserialización si no hay ninguna propiedad Date
en el archivo JSON, elija una de las siguientes opciones:
- Use .NET 7 o una versión posterior del System.Text.Jsonpaquete y agregue el modificador
required
(disponible a partir de C# 11) o el atributo JsonRequiredAttribute a la propiedad. - Implemente un convertidor personalizado.
- Implemente una devolución de llamada
OnDeserialized
(.NET 6 y versiones posteriores).
El siguiente código de convertidor de ejemplo produce una excepción si no se establece la propiedad Date
cuando se completa la deserialización:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Deserialize.
WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader)!;
// Check for required fields set by values in JSON
return forecast!.Date == default
? throw new JsonException("Required property not received in the JSON")
: forecast;
}
public override void Write(
Utf8JsonWriter writer,
WeatherForecast forecast, JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Serialize.
JsonSerializer.Serialize(writer, forecast);
}
}
}
Registre este convertidor personalizado agregando el convertidor a la colección JsonSerializerOptions.Converters.
Este patrón de llamada recursiva al convertidor exige que se registre el convertidor mediante JsonSerializerOptions, no mediante un atributo. Si registra el convertidor mediante un atributo, el convertidor personalizado se llama a sí mismo de forma recursiva. El resultado es un bucle infinito que finaliza en una excepción de desbordamiento de pila.
Al registrar el convertidor mediante el objeto de opciones, evite un bucle infinito; para ello, no pase el objeto de opciones cuando llame a Serialize o Deserialize de forma recursiva. El objeto de opciones contiene la colección Converters. Si lo pasa a Serialize
o Deserialize
, el convertidor personalizado se llama a sí mismo, con lo que se crea un bucle infinito que produce una excepción de desbordamiento de pila. Si las opciones predeterminadas no son factibles, cree una nueva instancia de las opciones con la configuración que necesite. Este enfoque será lento, ya que cada instancia nueva se almacena en caché de forma independiente.
Existe un patrón alternativo que puede usar el registro de JsonConverterAttribute
en la clase que se va a convertir. En este enfoque, el código del convertidor llama a Serialize
o Deserialize
en una clase que deriva de la clase que se va a convertir. La clase derivada no tiene ningún elemento JsonConverterAttribute
aplicado. En el siguiente ejemplo de esta alternativa:
WeatherForecastWithRequiredPropertyConverterAttribute
es la clase que se va a deserializar y a la que se le ha aplicadoJsonConverterAttribute
.WeatherForecastWithoutRequiredPropertyConverterAttribute
es la clase derivada que no tiene el atributo del convertidor.- El código del convertidor llama a
Serialize
yDeserialize
enWeatherForecastWithoutRequiredPropertyConverterAttribute
para evitar un bucle infinito. Hay un costo de rendimiento en este enfoque aplicado a la serialización debido a una creación de instancias de objeto adicional y la copia de valores de propiedad.
Estos son los tipos WeatherForecast*
:
[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
public class WeatherForecastWithoutRequiredPropertyConverterAttribute :
WeatherForecastWithRequiredPropertyConverterAttribute
{
}
Y este es el convertidor:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
{
public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// OK to pass in options when recursively calling Deserialize.
WeatherForecastWithRequiredPropertyConverterAttribute forecast =
JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
ref reader,
options)!;
// Check for required fields set by values in JSON.
return forecast!.Date == default
? throw new JsonException("Required property not received in the JSON")
: forecast;
}
public override void Write(
Utf8JsonWriter writer,
WeatherForecastWithRequiredPropertyConverterAttribute forecast,
JsonSerializerOptions options)
{
var weatherForecastWithoutConverterAttributeOnClass =
new WeatherForecastWithoutRequiredPropertyConverterAttribute
{
Date = forecast.Date,
TemperatureCelsius = forecast.TemperatureCelsius,
Summary = forecast.Summary
};
// OK to pass in options when recursively calling Serialize.
JsonSerializer.Serialize(
writer,
weatherForecastWithoutConverterAttributeOnClass,
options);
}
}
}
El convertidor de propiedades necesario requeriría lógica adicional en el caso de necesitar administrar atributos como [JsonIgnore] u otras opciones, como codificadores personalizados. Además, el código de ejemplo no controla las propiedades para las que se establece un valor predeterminado en el constructor, y este enfoque no distingue entre los siguientes escenarios:
- Falta una propiedad en el objeto JSON.
- Hay una propiedad para un tipo que no acepta valores NULL en el objeto JSON, pero el valor es el predeterminado para el tipo, como cero para
int
. - Hay una propiedad para un tipo de valor que acepta valores NULL en el objeto JSON, pero el valor es NULL.
Nota
Si usa System.Text.Json desde un controlador de ASP.NET Core, podría usar un atributo [Required]
en las propiedades de la clase de modelo en lugar de implementar un convertidor System.Text.Json.
Especificación del formato de fecha
Newtonsoft.Json
proporciona varias maneras de controlar cómo se serializan y deserializan las propiedades de los tipos DateTime
y DateTimeOffset
:
- El valor
DateTimeZoneHandling
se puede usar para serializar todos los valoresDateTime
como fechas UTC. - El valor
DateFormatString
y los convertidores deDateTime
se pueden usar para personalizar el formato de las cadenas de fecha.
System.Text.Json admite ISO 8601-1:2019, incluido el perfil RFC 3339. Este formato está ampliamente adoptado, no es ambiguo, y hace que los recorridos de ida y vuelta se realicen con precisión. Para usar cualquier otro formato, cree un convertidor personalizado. Por ejemplo, los siguientes convertidores serializan y deserializan JSON que usa el formato de época de Unix con o sin un desplazamiento de zona horaria (valores como /Date(1590863400000-0700)/
o /Date(1590863400000)/
):
sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
|| !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
|| !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
{
throw new JsonException();
}
int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);
return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
TimeSpan utcOffset = value.Offset;
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");
writer.WriteStringValue(formatted);
}
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);
if (
!match.Success
|| !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
{
throw new JsonException();
}
return s_epoch.AddMilliseconds(unixTime);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
writer.WriteStringValue(formatted);
}
}
Para obtener más información, consulte Compatibilidad con DateTime y DateTimeOffset en System.Text.Json.
Devoluciones de llamada
Newtonsoft.Json
le permite ejecutar código personalizado en varios puntos en el proceso de serialización o deserialización:
- OnDeserializing: al empezar a deserializar un objeto
- OnDeserialized: al finalizar la deserialización de un objeto
- OnSerializing: al empezar a serializar un objeto
- OnSerialized: al finalizar la serialización de un objeto
System.Text.Json expone las mismas notificaciones durante la serialización y la deserialización. Para usarlas, implemente una o varias de las siguientes interfaces del espacio de nombres System.Text.Json.Serialization:
Este es un ejemplo que comprueba si hay una propiedad null y escribe mensajes al principio y al final de la serialización y la deserialización:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Callbacks
{
public class WeatherForecast :
IJsonOnDeserializing, IJsonOnDeserialized,
IJsonOnSerializing, IJsonOnSerialized
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
void IJsonOnDeserialized.OnDeserialized()
{
Validate();
Console.WriteLine("Finished deserializing");
}
void IJsonOnSerializing.OnSerializing()
{
Console.WriteLine("Begin serializing");
Validate();
}
void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");
private void Validate()
{
if (Summary is null)
{
Console.WriteLine("The 'Summary' property is 'null'.");
}
}
}
public class Program
{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
};
string jsonString = JsonSerializer.Serialize(weatherForecast);
Console.WriteLine(jsonString);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
Console.WriteLine($"Date={weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary={weatherForecast?.Summary}");
}
}
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}
//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=
El código OnDeserializing
no tiene acceso a la nueva instancia POCO. Para manipular la nueva instancia POCO al inicio de la deserialización, coloque ese código en el constructor POCO.
Captadores y establecedores de propiedad no públicos
Newtonsoft.Json
puede usar captadores y establecedores de propiedades internos y privados a través del atributo JsonProperty
.
System.Text.Json admite captadores y establecedores de propiedades internos y privados a través del atributo [JsonInclude]. Para obtener código de ejemplo, vea Descriptores de acceso de propiedad no públicos.
Rellenar objetos existentes
El método JsonConvert.PopulateObject
de Newtonsoft.Json
deserializa un documento JSON en una instancia existente de una clase, en lugar de crear una nueva instancia. System.Text.Json siempre crea una nueva instancia del tipo de destino mediante el constructor sin parámetros público predeterminado. Los convertidores personalizados se pueden deserializar en una instancia existente.
Reutilización en lugar de reemplazo de propiedades
A partir de .NET 8, System.Text.Json admite la reutilización de propiedades inicializadas en lugar de reemplazarlas. Hay algunas diferencias en el comportamiento, que puede leer en la propuesta de API.
Para obtener más información, vea Rellenar las propiedades inicializadas.
La configuración de ObjectCreationHandling
en Newtonsoft.Json
permite especificar que los objetos de las propiedades deben reutilizarse en lugar de reemplazarse durante la deserialización. System.Text.Json siempre reemplaza los objetos en las propiedades. Los convertidores personalizados pueden proporcionar esta funcionalidad o puede actualizar a .NET 8, lo que proporciona funcionalidad de relleno.
Rellenar las propiedades sin establecedores
A partir de .NET 8, System.Text.Json admite la rellenación de propiedades, incluidas las que no tienen un establecedor. Para obtener más información, vea Rellenar las propiedades inicializadas.
Durante la deserialización, Newtonsoft.Json
agrega objetos a una colección, incluso si la propiedad no tiene ningún establecedor. System.Text.Json omite las propiedades que no tienen establecedores. Los convertidores personalizados pueden proporcionar esta funcionalidad o puede actualizar a .NET 8, que puede rellenar las propiedades de solo lectura.
Directiva de nomenclatura con palabras unidas por guiones bajos
System.Text.Json incluye una directiva de nomenclatura integrada para palabras unidas por guiones bajos y en minúsculas. Sin embargo, hay algunas diferencias de comportamiento con Newtonsoft.Json
para algunas entradas. En la tabla siguiente, se muestran algunas de estas diferencias al convertir la entrada mediante la directiva JsonNamingPolicy.SnakeCaseLower.
Entrada | Newtonsoft.Json resultado | System.Text.Json resultado |
---|---|---|
"AB1" | "a_b1" | "ab1" |
"SHA512Managed" | "sh_a512_managed" | "sha512_managed" |
"abc123DEF456" | "abc123_de_f456" | "abc123_def456" |
"KEBAB-CASE" | "keba_b-_case" | "kebab-case" |
La única directiva de nomenclatura de propiedades integrada en System.Text.Json es para palabras con las letras iniciales en mayúsculas. Newtonsoft.Json
puede convertir nombres de propiedad en palabras unidas por guiones bajos. Una directiva de nomenclatura personalizada puede proporcionar esta funcionalidad o actualizar a .NET 8 o posterior, lo que incluye directivas de nomenclatura de palabras unidas por guiones bajos y en minúsculas.
Atributos de System.Runtime.Serialization
Atributos System.Runtime.Serialization como DataContractAttribute, DataMemberAttribute y IgnoreDataMemberAttribute permiten definir un contrato de datos. Un contrato de datos es un acuerdo formal entre un servicio y un cliente que abstractamente describe los datos que se van a intercambiar. El contrato de datos define precisamente qué propiedades se serializan para el intercambio.
System.Text.Json no tiene compatibilidad integrada con estos atributos. Sin embargo, a partir de .NET 7, puede usar una resolución de tipos personalizada para agregar compatibilidad. Para ver un ejemplo, consulte ZCS.DataContractResolver.
Números octales
Newtonsoft.Json
trata los números con un cero a la izquierda como números octales. System.Text.Json no permite ceros a la izquierda porque la especificación RFC 8259 no los permite.
Control de los miembros que faltan
Newtonsoft.Json
se puede configurar para producir excepciones durante la deserialización si el archivo JSON incluye propiedades que faltan en el tipo de destino. De forma predeterminada, System.Text.Json omite las propiedades adicionales en el archivo JSON, excepto cuando se usa el atributo [JsonExtensionData].
En .NET 8 y versiones posteriores, puede establecer su preferencia para omitir o no permitir propiedades JSON no asignadas mediante uno de los siguientes medios:
- Aplique el atributo JsonUnmappedMemberHandlingAttribute al tipo al que va a deserializar.
- Para establecer sus preferencias globalmente, establezca la propiedad JsonSerializerOptions.UnmappedMemberHandling. O bien, para la generación de origen, establezca la propiedad JsonSourceGenerationOptionsAttribute.UnmappedMemberHandling y aplique el atributo a la clase JsonSerializerContext.
- Personalice la propiedad JsonTypeInfo.UnmappedMemberHandling.
JsonObjectAttribute
Newtonsoft.Json
tiene un atributo, JsonObjectAttribute
, que puede aplicarse en el nivel de tipo para controlar qué miembros se serializan, cómo se controlan los valores null
y si todos los miembros son obligatorios. System.Text.Json no tiene ningún atributo equivalente que se pueda aplicar en un tipo. Para algunos comportamientos, como el control de valores null
, puede configurar el mismo comportamiento en el JsonSerializerOptions global o individualmente en cada propiedad.
Considere el ejemplo siguiente que usa Newtonsoft.Json.JsonObjectAttribute
para especificar que se deben omitir todas las propiedades null
:
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }
En System.Text.Json, puede establecer el comportamiento de todos los tipos y propiedades:
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string json = JsonSerializer.Serialize<Person>(person, options);
O bien, puede establecer el comportamiento en cada propiedad por separado:
public class Person
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Name { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? Age { get; set; }
}
A continuación, considere el ejemplo siguiente que usa Newtonsoft.Json.JsonObjectAttribute
para especificar que todas las propiedades de miembro deben estar presentes en el JSON:
[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }
Para lograr el mismo comportamiento, System.Text.Json agregue el modificador de C# required
o JsonRequiredAttribute a cada propiedad. Para más información, consulte Propiedades necesarias.
public class Person
{
[JsonRequired]
public string? Name { get; set; }
public required int? Age { get; set; }
}
TraceWriter
Newtonsoft.Json
le permite realizar la depuración mediante el uso de un TraceWriter
para ver los registros generados por la serialización o deserialización. System.Text.Json no realiza el registro.
JsonDocument y JsonElement en comparación con JToken (como JObject, JArray)
System.Text.Json.JsonDocument proporciona la capacidad de analizar y compilar un Document Object Model (DOM) de solo lectura a partir de cargas JSON existentes. DOM proporciona acceso aleatorio a los datos en una carga JSON. A los elementos JSON que componen los datos se puede acceder mediante el tipo JsonElement. El tipo JsonElement
proporciona las API para convertir texto JSON en tipos comunes de .NET. JsonDocument
expone una propiedad RootElement.
A partir de .NET 6, puede analizar y compilar un DOM mutable a partir de cargas JSON existentes mediante el tipo JsonNode y los demás tipos del espacio de nombres System.Text.Json.Nodes. Para más información, consulte Uso de JsonNode
.
JsonDocument es IDisposable
JsonDocument
compila una vista en memoria de los datos en un búfer agrupado. Por lo tanto, a diferencia de JObject
o JArray
de Newtonsoft.Json
, el tipo JsonDocument
implementa IDisposable
y debe usarse dentro de un bloque Using. Para más información, consulte JsonDocument es IDisposable.
JSonDocument es de solo lectura
El DOM System.Text.Json no puede agregar, quitar o modificar elementos JSON. Está diseñado de esta manera para favorecer el rendimiento y para reducir las asignaciones para el análisis de los tamaños comunes de carga de JSON (es decir, < 1 MB).
JsonElement es una estructura de unión
JsonDocument
expone el RootElement
como una propiedad de tipo JsonElement (que es una unión), un tipo de estructura que abarca cualquier elemento JSON. Newtonsoft.Json
utiliza tipos jerárquicos dedicados como JObject
, JArray
, JToken
, etc. JsonElement
es lo que puede buscar y enumerar, y puede usar JsonElement
para materializar los elementos JSON en tipos de .NET.
A partir de .NET 6, puede usar el tipo JsonNode y los tipos del espacio de nombres System.Text.Json.Nodes que se corresponden con JObject
, JArray
y JToken
. Para más información, consulte Uso de JsonNode
.
Cómo buscar subelementos en JsonDocument y JsonElement
Las búsquedas de tokens JSON mediante JObject
o JArray
desde Newtonsoft.Json
suelen ser relativamente rápidas porque son búsquedas en algunos diccionarios. Por comparación, las búsquedas en JsonElement
requieren una búsqueda secuencial de las propiedades y, por lo tanto, son relativamente lentas (por ejemplo, al usar TryGetProperty
). System.Text.Json está diseñado para minimizar el tiempo de análisis inicial en lugar del tiempo de búsqueda. Para más información, consulte Cómo buscar subelementos en JsonDocument y JsonElement.
Utf8JsonReader vs. JsonTextReader
System.Text.Json.Utf8JsonReader es un lector de solo avance, de baja asignación y de alto rendimiento para texto JSON con codificación UTF-8 que se lee desde ReadOnlySpan<byte> o ReadOnlySequence<byte>. Utf8JsonReader
es un tipo de bajo nivel que se puede usar para compilar analizadores y deserializadores personalizados.
Utf8JsonReader es una estructura de referencia
El objeto JsonTextReader
de Newtonsoft.Json
es una clase. El tipo Utf8JsonReader
difiere en que es una estructura de referencia. Para obtener más información, consulte Limitaciones de estructura de referencia para Utf8JsonReader.
Lectura de valores NULL en tipos de valor que aceptan valores NULL
Newtonsoft.Json
proporciona las API que devuelven Nullable<T>, como ReadAsBoolean
, que controla un TokenType
Null
por usted devolviendo un valor bool?
. Las API integradas de System.Text.Json
solo devuelven tipos de valor que no aceptan valores NULL. Para más información, consulte Lectura de valores NULL en tipos de valor que aceptan valores NULL.
Multi-target para leer JSON
Si necesita seguir usando Newtonsoft.Json
para determinadas plataformas de destino, puede tener varias versiones y dos implementaciones. Pero esto no es algo trivial y requeriría algunos #ifdefs
y la duplicación de orígenes. Una manera de compartir todo el código posible es crear un contenedor ref struct
alrededor de Utf8JsonReader y Newtonsoft.Json.JsonTextReader
. Dicho contenedor unificaría el área expuesta pública mientras aísla las diferencias de comportamiento. Esto le permite aislar los cambios principalmente en la construcción del tipo, junto con pasar el nuevo tipo por referencia. Este es el patrón que sigue la biblioteca de Microsoft.Extensions.DependencyModel:
Utf8JsonWriter vs. JsonTextWriter
System.Text.Json.Utf8JsonWriter ofrece una forma de escribir texto JSON con codificación UTF-8 de alto rendimiento a partir de tipos de .NET comunes como String
, Int32
y DateTime
. El escritor es un tipo de bajo nivel que se puede usar para compilar serializadores personalizados.
Escritura de valores sin formato
Newtonsoft.Json
tiene un método WriteRawValue
que escribe JSON sin procesar donde se espera un valor. System.Text.Json tiene un equivalente directo: Utf8JsonWriter.WriteRawValue. Para más información, consulte Escritura de JSON sin formato.
Personalización del formato JSON
JsonTextWriter
incluye la configuración siguiente, para la cual Utf8JsonWriter no tiene ningún equivalente:
- QuoteChar: especifica el carácter que se va a usar para rodear los valores de cadena.
Utf8JsonWriter
siempre usa comillas dobles. - QuoteName: especifica si los nombres de propiedad deben encerrarse entre comillas.
Utf8JsonWriter
siempre los coloca entrecomillados.
A partir de .NET 9, puede personalizar el tamaño y el carácter de sangría para Utf8JsonWriter mediante las opciones expuestas por la estructura de JsonWriterOptions :
JsonTextWriter
incluye la configuración siguiente, para la cual Utf8JsonWriter
no tiene ningún equivalente:
- Indentation: especifica el número de caracteres a los que se va a aplicar sangría.
Utf8JsonWriter
siempre aplica una sangría a 2 caracteres. - IndentChar: especifica el carácter que se va a utilizar para la sangría.
Utf8JsonWriter
siempre usa el espacio en blanco. - QuoteChar: especifica el carácter que se va a usar para rodear los valores de cadena.
Utf8JsonWriter
siempre usa comillas dobles. - QuoteName: especifica si los nombres de propiedad deben encerrarse entre comillas.
Utf8JsonWriter
siempre los coloca entrecomillados.
No hay ninguna solución alternativa que permita personalizar el JSON generado por Utf8JsonWriter
de estas maneras.
Escritura de valores TimeSpan, URI o char
JsonTextWriter
proporciona métodos WriteValue
para los valores TimeSpan, URIy char. Utf8JsonWriter
no tiene métodos equivalentes. En su lugar, dé formato a estos valores como cadenas (por ejemplo, llamando a ToString()
) y llame a WriteStringValue.
Multi-target para escribir JSON
Si necesita seguir usando Newtonsoft.Json
para determinadas plataformas de destino, puede tener varias versiones y dos implementaciones. Pero esto no es algo trivial y requeriría algunos #ifdefs
y la duplicación de orígenes. Una manera de compartir todo el código posible es crear un contenedor alrededor de Utf8JsonWriter y Newtonsoft.Json.JsonTextWriter
. Dicho contenedor unificaría el área expuesta pública mientras aísla las diferencias de comportamiento. Esto le permite aislar los cambios principalmente en la construcción del tipo. La biblioteca de Microsoft.Extensions.DependencyModel sigue:
TypeNameHandling.All no se admite
La decisión de excluir la TypeNameHandling.All
funcionalidad -equivalent de System.Text.Json
fue intencionada. Permitir que una carga JSON especifique su propia información de tipo es causa habitual de vulnerabilidades en las aplicaciones web. En concreto, la configuración de Newtonsoft.Json
con TypeNameHandling.All
permite al cliente remoto insertar una aplicación ejecutable completa dentro de la propia carga JSON, de modo que, durante la deserialización, la aplicación web extraiga y ejecute el código insertado. Para más información, consulte Ataques de viernes 13 a JSON en PowerPoint y Detalles de los ataques de viernes 13 a JSON.
No se admiten consultas de ruta de acceso JSON
El DOM JsonDocument
no admite consultas mediante la ruta de acceso JSON.
En un DOM JsonNode, cada instancia de JsonNode
tiene un método GetPath
que devuelve una ruta de acceso a ese nodo. Pero no hay ninguna API integrada para controlar las consultas basadas en cadenas de consulta de rutas de acceso JSON.
Para más información, consulte la incidencia de GitHub dotnet/runtime n.º 31068.
Algunos límites no configurables
System.Text.Json establece límites que no se pueden cambiar para algunos valores, como el tamaño máximo del token en caracteres (166 MB) y en base 64 (125 MB). Para más información, consulte JsonConstants
en el código fuente y la incidencia de GitHub dotnet/runtime n.º 39953.
NaN, Infinity, -Infinity
Newtonsoft analiza los tokens de cadena JSON NaN
, Infinity
y -Infinity
. Con System.Text.Json, use JsonNumberHandling.AllowNamedFloatingPointLiterals. Para más información sobre cómo usar esta configuración, consulte Permitir o escribir números entre comillas.
Uso de GitHub Copilot para migrar
Puede obtener ayuda de codificación de GitHub Copilot para convertir el código en el IDE.
Si usa Visual Studio 2022, versión 17.8 o posterior, puede probar GitHub Copilot controlado por IA en Visual Studio para ayudar a migrar el código de Newtonsoft.Json
para usar System.Text.Json
. Envíe su pregunta y el código para convertir como una solicitud en la ventana de chat de Copilot, como en el ejemplo siguiente.
Nota:
GitHub Copilot funciona con IA, por lo que es posible que se produzcan sorpresas y errores. Asegúrese de comprobar cualquier código o sugerencia generado. Para obtener más información sobre el uso general de GitHub Copilot, impacto en el producto, supervisión humana y privacidad, consulte Preguntas más frecuentes sobre GitHub Copilot.
Ejemplo de mensaje para el chat de Copilot
convert the following code to use System.Text.Json
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);
Ejemplo de respuesta del chat de Copilot
To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;
public class Product
{
public string Name { get; set; }
public DateTime ExpiryDate { get; set; }
public decimal Price { get; set; }
public string[] Sizes { get; set; }
}
public class Program
{
public static void Main()
{
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string output = JsonSerializer.Serialize(product);
Console.WriteLine(output);
}
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.
Tenga en cuenta que los resultados pueden ser diferentes de lo que se muestra en estas respuestas de ejemplo. Los modelos de IA no son deterministas, lo que significa que pueden devolver respuestas diferentes cuando se le haga la misma pregunta. Esto puede deberse al aprendizaje y la adaptación adicionales a lo largo del tiempo, la variación del lenguaje, los cambios en el contexto, como el historial de chat, etc.
Puede usar características de chat, como comandos de barra diagonal, referencias y subprocesos, para establecer la intención y obtener mejores respuestas con contexto con ámbito.
Por ejemplo, si el archivo de código filename
está abierto en el IDE, puede hacer referencia al archivo en el símbolo del sistema al chat de Copilot con "convertir #filename
para usar System.Text.Json
". O bien, puede hacer referencia a la solución con "convertir @workspace
para usar System.Text.Json
" en la ventana de chat o en el chat en línea.