【问题标题】:Deserializing Indexed Properties into an Array将索引属性反序列化为数组
【发布时间】:2016-06-20 19:50:52
【问题描述】:

假设我有一些 Json 会以这样的数据包形式出现:

{
    "LinkType1": "google",
    "LinkUrl1": "https://plus.google.com/test",
    "LinkShow1": 1,

    "LinkType2": "facebook",
    "LinkUrl2": "https://www.facebook.com/test",
    "LinkShow2": 0,

    "LinkType3": "linkedin",
    "LinkUrl3": "http://www.linkedin.com/test",
    "LinkShow3": 1,

    "count": 3,
    "errorCode": 0,
    "errorMessage": "Success"
}

注意所有东西是如何作为相同的属性返回的,但上面有一个索引?

我希望能够反序列化该数据,就好像它是一个数组而不是单个属性一样。将其反序列化为以下类的最佳方法是什么?我正在使用 Newtonsoft Json 库进行序列化,因此首选使用它的解决方案。

    public class LinksResult
    {
        public List<LinkData> Links { get; set; } 

        [JsonProperty("count")]
        public int Count { get; set; }

        [JsonProperty("errorCode")]
        public int ErrorCode { get; set; }

        [JsonProperty("errorMessage")]
        public string ErrorMessage { get; set; }
    }

    public class LinkData
    {
        public string LinkType { get; set; }
        public string LinkUrl { get; set; }
        public bool LinkShow { get; set; }
    }

【问题讨论】:

    标签: c# json serialization json.net deserialization


    【解决方案1】:

    您可以使用自定义 JsonConverter 将 JSON 数据反序列化为所需的结构。下面是转换器的代码。

    class LinksResultConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(LinksResult));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject obj = JObject.Load(reader);
            LinksResult result = new LinksResult();
            result.Count = (int)obj["count"];
            result.ErrorCode = (int)obj["errorCode"];
            result.ErrorMessage = (string)obj["errorMessage"];
            result.Links = new List<LinkData>();
    
            for (int i = 1; i <= result.Count; i++)
            {
                string index = i.ToString();
                LinkData link = new LinkData();
                link.LinkType = (string)obj["LinkType" + index];
                link.LinkUrl = (string)obj["LinkUrl" + index];
                link.LinkShow = (int)obj["LinkShow" + index] == 1;
                result.Links.Add(link);
            }
    
            return result;
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    要使用转换器,只需将[JsonConverter] 属性添加到您的LinksResult 类,如下所示。 (请注意,这种方法不需要 [JsonProperty] 属性,因为 JSON 属性名称和实际类成员之间的映射由转换器直接处理。)

    [JsonConverter(typeof(LinksResultConverter))]
    public class LinksResult
    {
        public List<LinkData> Links { get; set; }
        public int Count { get; set; }
        public int ErrorCode { get; set; }
        public string ErrorMessage { get; set; }
    }
    

    然后,你可以像这样反序列化:

    LinksResult result = JsonConvert.DeserializeObject<LinksResult>(json);
    

    小提琴:https://dotnetfiddle.net/56b34H

    【讨论】:

    • 谢谢;很高兴我能帮上忙。
    • 嘿,您的回答帮助我制作了一个非常不错的通用版本。我非常感谢这个方向!
    【解决方案2】:

    Brian 的回答非常好,它让我达到了 80% 的目标。但是,如果这种模式发生在许多不同的对象上,那么一遍又一遍地使用它并不是一个很好的实现。

    我做了一些更通用的东西。 “页面”应具有的界面。

    public interface IPage<TItem>
    {
        int Count { get; set; }
        List<TItem> PageItems { get; set; }
    }
    

    然后是页面转换器本身。

    public class PageConverter<TPage, TItem> : JsonConverter
            where TPage : IPage<TItem>, new()
            where TItem : new()
    {
        private readonly Regex _numberPostfixRegex = new Regex(@"\d+$");
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(TPage));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var obj = serializer.Deserialize<JObject>(reader);
            var page = new TPage();
            serializer.Populate(obj.CreateReader(), page); //Loads everything that isn't a part of the items. 
            page.PageItems = new List<TItem>();
    
            for (int i = 1; i <= page.Count; i++)
            {
                string index = i.ToString();
    
                //Find all properties that have a number at the end, then any of those that are the same number as the current index.
                //Put those in a new JObject.
                var jsonItem = new JObject();
                foreach (var prop in obj.Properties().Where(p => _numberPostfixRegex.Match(p.Name).Value == index))
                {
                    jsonItem[_numberPostfixRegex.Replace(prop.Name, "")] = prop.Value;
                }
    
                //Deserialize and add to the list.
                TItem item = jsonItem.ToObject<TItem>(serializer);
                page.PageItems.Add(item);
            }
    
            return page;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    那么所有需要的就是在链接结果上实现它:

    [JsonConverter(typeof(PageConverter<LinksResult, LinkData>))]
    public class LinksResult : IPage<LinkData>
    {
        public int Count { get; set; }
    
        public List<LinkData> PageItems { get; set; }
    }
    

    我发现你可以control the serialization of capitalization with JsonSerializerSettings,所以最好把这个细节留给所选的序列化程序,而不是我的转换器。

    在这里提琴:https://dotnetfiddle.net/7KhwYY

    【讨论】:

      【解决方案3】:

      这是您可以应用的类似解决方案。请参阅 Serialize json to an object with catch all dictionary property David Hoerster 的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-13
        • 1970-01-01
        • 1970-01-01
        • 2012-08-19
        • 1970-01-01
        • 2020-03-18
        • 2013-04-09
        相关资源
        最近更新 更多