如何使用动态(数字)键名称反序列化子对象?

如何在.net中使用newtonsoft json.net反序列化json结构。

{ "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" } } } 

我试过下面的代码,但它不起作用。 我收到错误’错误转换值“test”键入’ConsoleApplication2.User’。 路径’users.parentname’,第5行,第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; } } 

请建议。

你有几个问题:

  • 您的JSON具有额外的嵌套级别,根对象包含单个属性"users"

     { "users" : { ... } } 

    您的数据模型需要反映这一点。

  • 您的"users"对象包含已知和未知属性名称的混合。 使用已知和未知字段反序列化json的问题解决了类似情况,但是在您的情况下,您的未知属性始终具有固定模式,并且应将其值反序列化为POCO字典 – 特别是User类。 因此,那里的答案不能完全满足您的需求,内置function也没有[JsonExtensionData]

以下转换器允许将未知属性反序列化为类型化容器,而不是任意类型的字典:

 [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; } } 

然后在您的类中使用它,如下所示:

 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; } } 

我以相当一般的方式编写了转换器,因此可以重复使用。 为Users类型硬编码的转换器将需要更少的代码。

你的Json必须看起来像这样:

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

为了使用您给定的类结构反序列化它:

 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; } } 

现在,您可以使用以下命令反序列化Json字符串:

 var root = JsonConvert.DeserializeObject(json);