serializar - serialize json to object c#
JSON.net: ¿cómo deserializar sin usar el constructor predeterminado? (2)
Json.Net prefiere usar el constructor predeterminado (sin parámetros) en un objeto si hay uno. Si hay varios constructores y desea que Json.Net utilice uno no predeterminado, puede agregar el atributo [JsonConstructor]
al constructor al que desea que Json.Net llame.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
Es importante que los nombres de los parámetros del constructor coincidan con los nombres de las propiedades correspondientes del objeto JSON (ignorando el caso) para que esto funcione correctamente. Sin embargo, no necesariamente tiene que tener un parámetro constructor para cada propiedad del objeto. Para aquellas propiedades de objeto JSON que no están cubiertas por los parámetros del constructor, Json.Net intentará utilizar los accesadores de propiedad pública (o las propiedades / campos marcados con [JsonProperty]
) para poblar el objeto después de construirlo.
Si no desea agregar atributos a su clase o no controla el código fuente de la clase que intenta deserializar, entonces otra alternativa es crear un JsonConverter personalizado para instanciar y poblar su objeto. Por ejemplo:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Luego, agregue el convertidor a su configuración de serializador, y use la configuración cuando deserialice:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
Tengo una clase que tiene un constructor predeterminado y también un constructor sobrecargado que toma un conjunto de parámetros. Estos parámetros coinciden con los campos en el objeto y se asignan en la construcción. En este punto, necesito el constructor predeterminado para otros fines, así que me gustaría conservarlo si puedo.
Mi problema: si elimino el constructor predeterminado y paso la cadena JSON, el objeto se deserializa correctamente y pasa los parámetros del constructor sin ningún problema. Termino recuperando el objeto poblado de la manera que esperaría. Sin embargo, tan pronto como agregue el constructor predeterminado al objeto, cuando llamo a JsonConvert.DeserializeObject<Result>(jsontext)
las propiedades ya no se rellenan.
En este punto, he intentado agregar new JsonSerializerSettings(){CheckAdditionalContent = true}
a la llamada de deserialización. eso no hizo nada.
Otra nota. los parámetros del contructor coinciden exactamente con los nombres de los campos, excepto que los parámetros comienzan con una letra minúscula. No creo que esto importe ya que, como mencioné, la deserialización funciona bien sin un constructor predeterminado.
Aquí hay una muestra de mis constructores:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Un poco tarde y no exactamente adecuado aquí, pero voy a agregar mi solución aquí, porque mi pregunta se ha cerrado como un duplicado de esta, y porque esta solución es completamente diferente.
Necesitaba una forma general de Json.NET
a Json.NET
que prefiera el constructor más específico para un tipo de estructura definido por el usuario, de modo que pueda omitir los atributos de JsonConstructor
que agregarían una dependencia al proyecto donde se define cada estructura.
Invertí un poco la ingeniería e implementé un sistema de resolución de contratos personalizado donde anulé el método CreateObjectContract
para agregar mi lógica de creación personalizada.
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
Lo estoy usando así.
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");