将索引属性反序列化为数组

时间:2016-06-20 19:50:52

标签: c# json serialization json.net deserialization

假设我有一些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; }
    }

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

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

我做了一些更通用的东西。一个&#34; Page&#34;会的。

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

答案 2 :(得分:0)

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