¿Cómo deserializar un objeto secundario con nombres de teclas dinámicas (numéricas)?

¿Cómo puedo deserializar debajo de la estructura json usando newtonsoft json.net en .net?

{ "users" : { "parentname":"test", "100034" : { "name" : "tom", "state" : "WA", "id" : "cedf-c56f-18a4-4b1" }, "10045" : { "name" : "steve", "state" : "NY", "id" : "ebb2-92bf-3062-7774" }, "12345" : { "name" : "mike", "state" : "MA", "id" : "fb60-b34f-6dc8-aaf7" } } } 

Intenté el código siguiente pero no funciona. Recibí el error ‘Error converting value’ test ‘para escribir’ ConsoleApplication2.User ‘. Ruta ‘users.parentname’, línea 5, posición 35. ‘

 class Program { static void Main(string[] args) { string json = @" { ""users"": { ""parentname"":""test"", ""10045"": { ""name"": ""steve"", ""state"": ""NY"", ""id"": ""ebb2-92bf-3062-7774"" } } }"; RootObject root = JsonConvert.DeserializeObject(json); } } class RootObject { public string ParentName { get; set; } public Dictionary users { get; set; } } class User { public string name { get; set; } public string state { get; set; } public string id { get; set; } public string ParentName { get; set; } } 

Por favor recomiende.

Tienes un par de problemas:

  • Su JSON tiene un nivel adicional de anidación, con el objeto raíz que contiene una única propiedad "users" :

     { "users" : { ... } } 

    Su modelo de datos debe reflejar esto.

  • Su objeto "users" tiene una mezcla de nombres de propiedades conocidas y desconocidas. La pregunta Deserialize json con campos conocidos y desconocidos aborda una situación similar, sin embargo, en su caso, sus propiedades desconocidas siempre tienen un esquema fijo y sus valores deben deserializarse en un diccionario de POCO, específicamente la clase User . Por lo tanto, las respuestas allí no satisfacen completamente sus necesidades, ni la funcionalidad [JsonExtensionData] .

El siguiente convertidor permite que las propiedades desconocidas se deserialicen en un contenedor tipeado , en lugar de en un diccionario de tipos arbitrarios:

 [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] public class JsonTypedExtensionDataAttribute : Attribute { } public class TypedExtensionDataConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(TObject).IsAssignableFrom(objectType); } JsonProperty GetExtensionJsonProperty(JsonObjectContract contract) { try { return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single(); } catch (InvalidOperationException ex) { throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex); } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var jObj = JObject.Load(reader); var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var extensionJsonProperty = GetExtensionJsonProperty(contract); var extensionJProperty = (JProperty)null; for (int i = jObj.Count - 1; i >= 0; i--) { var property = (JProperty)jObj.AsList()[i]; if (contract.Properties.GetClosestMatchProperty(property.Name) == null) { if (extensionJProperty == null) { extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject()); jObj.Add(extensionJProperty); } ((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent()); } } var value = existingValue ?? contract.DefaultCreator(); using (var subReader = jObj.CreateReader()) serializer.Populate(subReader, value); return value; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType()); var extensionJsonProperty = GetExtensionJsonProperty(contract); JObject jObj; using (new PushValue(true, () => Disabled, (canWrite) => Disabled = canWrite)) { jObj = JObject.FromObject(value, serializer); } var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent(); if (extensionValue != null) { for (int i = extensionValue.Count - 1; i >= 0; i--) { var property = (JProperty)extensionValue.AsList()[i]; jObj.Add(property.RemoveFromLowestPossibleParent()); } } jObj.WriteTo(writer); } [ThreadStatic] static bool disabled; // Disables the converter in a thread-safe manner. bool Disabled { get { return disabled; } set { disabled = value; } } public override bool CanWrite { get { return !Disabled; } } public override bool CanRead { get { return !Disabled; } } } public struct PushValue : IDisposable { Action setValue; T oldValue; public PushValue(T value, Func getValue, Action setValue) { if (getValue == null || setValue == null) throw new ArgumentNullException(); this.setValue = setValue; this.oldValue = getValue(); setValue(value); } #region IDisposable Members // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class. public void Dispose() { if (setValue != null) setValue(oldValue); } #endregion } public static class JsonExtensions { public static TJToken RemoveFromLowestPossibleParent(this TJToken node) where TJToken : JToken { if (node == null) return null; var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault(); if (contained != null) contained.Remove(); // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should if (node.Parent is JProperty) ((JProperty)node.Parent).Value = null; return node; } public static IList AsList(this IList container) { return container; } } 

Luego úsala en tus clases de la siguiente manera:

 class RootObject { [JsonProperty("users")] public Users Users { get; set; } } [JsonConverter(typeof(TypedExtensionDataConverter))] class Users { public Users() { this.UserTable = new Dictionary(); } [JsonProperty("parentname")] public string ParentName { get; set; } [JsonTypedExtensionData] public Dictionary UserTable { get; set; } } class User { public string name { get; set; } public string state { get; set; } public string id { get; set; } } 

Escribí el convertidor de una manera bastante general para que pueda ser reutilizado. Un convertidor que está codificado para el tipo de Users requeriría menos código.

Tu Json tiene que verse así:

 { "ParentName":"test", "users":{ "10045":{ "name":"steve", "state":"NY", "id":"ebb2-92bf-3062-7774", "ParentName":"someOtherName" } } } 

Para deserializarlo con su estructura de clase dada:

 class RootObject { public string ParentName { get; set; } public Dictionary users { get; set; } } class User { public string name { get; set; } public string state { get; set; } public string id { get; set; } public string ParentName { get; set; } } 

Ahora puedes deserializar la cadena Json con:

 var root = JsonConvert.DeserializeObject(json);