2011-07-26 10 views
6

Me gustaría usar JSON.net para deserializar a un objeto pero poner propiedades no asignadas en una propiedad del diccionario. ¿Es posible?Serializar json a un objeto con capturar todas las propiedades del diccionario

Por ejemplo, dada la JSON,

{one:1,two:2,three:3} 

y la clase C#:

public class Mapped { 
    public int One {get; set;} 
    public int Two {get; set;} 
    public Dictionary<string,object> TheRest {get; set;} 
} 

Puede JSON.NET deserializar a una instancia con valores uno = 1, dos = 1, restode = Diccionario {{"three, 3}}

+0

Actualizado el código en mi respuesta para que sea más genérico. –

Respuesta

2

Puede crear un CustomCreationConverter para hacer lo que necesita hacer. Aquí hay una muestra (bastante fea, pero demuestra cómo puede querer hacerlo):

namespace JsonConverterTest1 
{ 
    public class Mapped 
    { 
     private Dictionary<string, object> _theRest = new Dictionary<string, object>(); 
     public int One { get; set; } 
     public int Two { get; set; } 
     public Dictionary<string, object> TheRest { get { return _theRest; } } 
    } 

    public class MappedConverter : CustomCreationConverter<Mapped> 
    { 
     public override Mapped Create(Type objectType) 
     { 
      return new Mapped(); 
     } 

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      var mappedObj = new Mapped(); 
      var objProps = objectType.GetProperties().Select(p => p.Name.ToLower()).ToArray(); 

      //return base.ReadJson(reader, objectType, existingValue, serializer); 
      while (reader.Read()) 
      { 
       if (reader.TokenType == JsonToken.PropertyName) 
       { 
        string readerValue = reader.Value.ToString().ToLower(); 
        if (reader.Read()) 
        { 
         if (objProps.Contains(readerValue)) 
         { 
          PropertyInfo pi = mappedObj.GetType().GetProperty(readerValue, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); 
          var convertedValue = Convert.ChangeType(reader.Value, pi.PropertyType); 
          pi.SetValue(mappedObj, convertedValue, null); 
         } 
         else 
         { 
          mappedObj.TheRest.Add(readerValue, reader.Value); 
         } 
        } 
       } 
      } 
      return mappedObj; 
     } 
    } 

    public class Program 
    { 
     static void Main(string[] args) 
     { 
      string json = "{'one':1, 'two':2, 'three':3, 'four':4}"; 

      Mapped mappedObj = JsonConvert.DeserializeObject<Mapped>(json, new MappedConverter()); 

      Console.WriteLine(mappedObj.TheRest["three"].ToString()); 
      Console.WriteLine(mappedObj.TheRest["four"].ToString()); 
     } 
    } 
} 

lo que la salida de mappedObj después de deserializar la cadena JSON será un objeto con sus propiedades OneTwo y pobladas, y todo lo demás puestos en el Dictionary. De acuerdo, codifiqué los valores Uno y Dos como int s, pero creo que esto demuestra cómo se haría esto.

Espero que esto ayude.

EDIT: He actualizado el código para hacerlo más genérico. No lo probé por completo, por lo que puede haber algunos casos en los que falla, pero creo que te lleva la mayor parte del camino hasta allí.

+0

David es genial, pero esperaba una solución más genérica. – PhilHoy

+0

Sí, lo armé rápidamente. No puedo volver a hacerlo en este momento, pero lo haré un poco más genérico en breve. Probablemente implicaría un poco de reflexión. Sin embargo, la estructura básica no cambiará, solo la lógica en el segundo bloque 'if (reader.Read())'. Pero espero que puedas ver a dónde voy con esto. Por cierto, pregunta genial que preguntaste. –

+0

Se actualizó el código para hacerlo más genérico para diferentes nombres y/o tipos de propiedades. –

1

La forma más fácil de hacer esto es usar el atributo JsonExtensionData para definir un diccionario catch all.

Ejemplo de the Json.Net documentation:

public class DirectoryAccount 
{ 
    // normal deserialization 
    public string DisplayName { get; set; } 

    // these properties are set in OnDeserialized 
    public string UserName { get; set; } 
    public string Domain { get; set; } 

    [JsonExtensionData] 
    private IDictionary<string, JToken> _additionalData; 

    [OnDeserialized] 
    private void OnDeserialized(StreamingContext context) 
    { 
     // SAMAccountName is not deserialized to any property 
     // and so it is added to the extension data dictionary 
     string samAccountName = (string)_additionalData["SAMAccountName"]; 

     Domain = samAccountName.Split('\\')[0]; 
     UserName = samAccountName.Split('\\')[1]; 
    } 

    public DirectoryAccount() 
    { 
     _additionalData = new Dictionary<string, JToken>(); 
    } 
} 

string json = @"{ 
    'DisplayName': 'John Smith', 
    'SAMAccountName': 'contoso\\johns' 
}"; 

DirectoryAccount account = JsonConvert.DeserializeObject<DirectoryAccount>(json); 

Console.WriteLine(account.DisplayName); 
// John Smith 

Console.WriteLine(account.Domain); 
// contoso 

Console.WriteLine(account.UserName); 
// johns 
Cuestiones relacionadas