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

时间:2016-10-17 14:23:09

标签: c# dynamic json.net key

如何在.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"
        }
    }
}

我尝试了下面的代码,但它不起作用。我收到错误'错误转换值"测试"输入' 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<RootObject>(json);
        }
    }

    class RootObject
    {
        public string ParentName { get; set; }
        public Dictionary<string, User> 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; }
    }

请建议。

2 个答案:

答案 0 :(得分:4)

你有几个问题:

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

    {
        "users" : { ... }
    }
    

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

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

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

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}

public class TypedExtensionDataConverter<TObject> : 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<bool>(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<T> : IDisposable
{
    Action<T> setValue;
    T oldValue;

    public PushValue(T value, Func<T> getValue, Action<T> 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<TJToken>(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<JToken> AsList(this IList<JToken> container) { return container; }
}

然后在课程中使用它,如下所示:

class RootObject
{
    [JsonProperty("users")]
    public Users Users { get; set; }
}

[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
    public Users()
    {
        this.UserTable = new Dictionary<string, User>();
    }

    [JsonProperty("parentname")]
    public string ParentName { get; set; }

    [JsonTypedExtensionData]
    public Dictionary<string, User> UserTable { get; set; }
}

class User
{
    public string name { get; set; }
    public string state { get; set; }
    public string id { get; set; }
}

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

答案 1 :(得分:0)

你的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<string, User> 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<RootObject>(json);