¿Cómo funciona la deserialización JSON en C#?
json.net (1)
Estoy tratando de entender cómo
JsonConvert.DeserializeObject<X>(someJsonString)
puede establecer los valores utilizando el constructor.
using Newtonsoft.json
public class X {
[JsonProperty("some_Property")]
public string SomeProperty {get;}
[JsonProperty("some_Property_2")]
public string SomeProperty2 {get;}
public X(string someProperty, string someProperty2) {
SomeProperty = someProperty;
SomeProperty2 = someProperty2;
}
public static X parseObject(string parseThisJson) {
JsonConvert.DeserializeObject<X>(someJsonString);
}
}
En el código anterior, quiero entender cómo JsonConvert.DeserializeObject es capaz de deserializarlo correctamente.
¿Json serialize utiliza este constructor
public X(string someProperty, string someProperty2)
?
Si es así, ¿cómo se llama y usa este constructor?
¿Qué sucederá si parseThisJson tiene más pares de valores clave además de some_Property y some_Property_2?
Después de Newtonsoft.Json en Newtonsoft.Json fuentes de Newtonsoft.Json , puedo decirte el algoritmo de Newtonsoft.Json de instancias de objetos que se usa allí. Y sí, el constructor casi siempre se llama (*). La pregunta es solo "¿cuál?". Aquí hay una versión colorida de la respuesta:
TL; DR En
primer lugar, Newtonsoft.Json crea
JsonContract
del tipo que va a deserializar.
Su clase abstracta.
Y tiene diferentes implementaciones para diccionarios, matrices, objetos, etc. En su caso, se creará
JsonObjectContract
.
El contrato contiene varios metadatos sobre el tipo deserializado.
Lo más interesante para nosotros son:
-
IsInstantiable
: define si el tipo deserializado es instanciable (ver más abajo) -
Properties
: su colección de propiedades de objeto -
DefaultCreator
: método de creación predeterminado utilizado para crear el objetoFunc<object>
-
DefaultCreatorNonPublic
: define si el constructor predeterminado no es público -
OverrideCreator
: creador no predeterminado, utilizado siJsonConstructorAttribute
se aplica al constructor del objeto -
ParametrizedCreator
: creador que llama al constructor paramterizado, se usa si no tenemos creadores predeterminados ni anuladores -
CreatorParameters
: colección de propiedades que se utilizan para anular el creador o el creador parametrizado. -
MemberSerialization
: este valor define la forma en que se serializan las propiedades y los campos. De forma predeterminada, se establece enOptOut
, es decir, todos los miembros públicos están serializados. Si desea excluir algunos, debe usar el atributoJsonIgnore
. Pero también existe la opciónFields
, que dice que todos los campos públicos y privados deben ser serializados. Hay varios para activar esta opción. Pero por defecto está deshabilitado.
Algunos de estos metadatos se pueden recuperar reflejando los metadatos de tipo.
Por ejemplo,
IsInstantiable
se calcula comprobando si el tipo deserializado no es abstracto y no es interfaz.
DefaultContractResolver
agrega algunos metadatos.
En particular, define la forma en que se debe construir el objeto.
En pseudocódigo:
if (contract.IsInstantiable)
{
if (type has default constructor or its a value type)
{
contract.DefaultCreator = get default (parameterless) constructor;
contract.DefaultCreatorNonPublic = check if default constructor public
}
if (we have constructor marked with JsonConstructorAttribute)
{
contract.OverrideCreator = constructor marked with attribute
contract.CreatorParameters = get properties which match constructor parameters
}
else if (contract.MemberSerialization == MemberSerialization.Fields)
{
// only if the upplication if fully trusted
contract.DefaultCreator = FormatterServices.GetUninitializedObject
}
else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic)
{
if (we have one public constructor with parameters)
{
contract.ParametrizedCreator = constructor with parameters;
contract.CreatorParameters = get properties which match ctor parameters
}
}
}
Entonces, como puede ver, prioirty va al constructor marcado con el atributo
JsonConstructorAttribute
.
También recibirá un error si hay más de un constructor de este tipo.
(*) El siguiente es el único caso en que se puede crear un objeto sin llamar al constructor.
Por ejemplo, si marcará la clase con el
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
para serializar campos privados.
Luego verificamos si tenemos un constructor sin parámetros predeterminado que no es privado. Si es así, entonces buscamos otro constructor, uno que tenga parámetros y debería ser público. Si hay más de un constructor de este tipo, también obtendrá un error.
Y lo último que hay que tener en cuenta:
CraeatorParameters
.
Newtonsoft.Json usa la reflexión para obtener los parámetros del constructor y luego trata de encontrar la coincidencia más cercana por nombre de estos parámetros del constructor con las propiedades del objeto.
También verifica el tipo de propiedad y los parámetros para que coincidan.
Si no se encuentra ninguna coincidencia, el valor predeterminado se pasará a este constructor parametrizado.