如何使用无意义的动态密钥名称反序列化根对象? Json.NET

时间:2017-01-07 00:22:40

标签: c# json json.net

我从REST请求中获取以下json有效负载。

{
    "version": "1.1", 
    "date": "2017-01-06",
    "count": "130",
    "0": {
          "artist": "Artist 1",
          "title": "Title 1"
     },
    "1": {
          "artist": "Artist 2",
          "title": "Title 2"
     },
    ...
    "49": {
          "artist": "Artist 50",
          "title": "Title 50"
     }
}

如您所见,有几个数字根元素。这些元素在响应中索引对象,因此我不需要数字本身,我只需要它们所代表的Song对象。我还需要count知道我是否需要拨打服务器来获取下一页歌曲(服务器每次调用最多返回50 Song个对象。)

以下是Song课程的内容:

public class Song
{
    public string artist {get; set;}
    public string title {get; set;}
}

但是我在获取根元素方面遇到了问题。我试图模仿有动态子元素时使用的过程,但这不起作用。

public class SongsResponse
{
    public string version { get; set; }
    public string date { get; set; }
    public string count {get; set; }
    public Dictionary<string, Song> songs { get; set; }
}

我已经看到过&#34;扔掉的解决方案&#34; versiondatecount以及没有预定义类的其他人。

但我需要count,理想情况下我希望能够使用预定义的类。

2 个答案:

答案 0 :(得分:1)

您不需要包含索引。歌曲的索引是因为它们将被反序列化为集合。像这样更改架构:

{  
   "version":"1.1",
   "date":"2017-01-06",
   "count":"130",
   "songs":[  
      {  
         "artist":"Artist 1",
         "title":"Title 1"
      },
      {  
         "artist":"Artist 2",
         "title":"Title 2"
      },
      {  
         "artist":"Artist 3",
         "title":"Title 3"
      }
   ]
}

public class Song
{
    public string artist {get; set;}
    public string title {get; set;}
}

public class SongsResponse
{
    public string version { get; set; }
    public string date { get; set; }
    public string count {get; set; }
    public List<Song> songs { get; set; }
}

更新

因此,您可能无法控制发送给您的json,但您仍然可以控制本地域对象以及如何反序列化json。我会坚持上面定义的类定义,并创建一个自定义JsonConverter,您可以使用它将响应反序列化为更有意义的内容。实际上,当你在这里时,你可以让你的SongsResponse课程变得更好。通过为datecount属性使用适当的类型(如果您不打算利用它,为什么要使用强类型语言)。

所以这堂课:

public class SongsResponseJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanWrite is false. The type will skip the converter.");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var result = new SongsResponse();

        var obj = JObject.Load(reader);
        result.version = obj["version"].Value<string>();
        result.date = obj["date"].Value<DateTime>();
        result.count = obj["count"].Value<int>();
        result.songs =
            obj
            .Properties()
            .Where(x => !(new string[] { "version", "date", "count" }.Contains(x.Name)))
            .Select(x => new { Id = int.Parse(x.Name), Song = x })
            .OrderBy(x => x.Id)
            .Select(x => x.Song.Value.ToObject<Song>())
            .ToList();
        return result;
    }

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(SongsResponse);
    }
}

您可以这样使用:

var json = @"
{
    ""version"": ""1.1"", 
    ""date"": ""2017-01-06"",
    ""count"": ""130"",
    ""0"": {
        ""artist"": ""Artist 1"",
          ""title"": ""Title 1""
     },
    ""1"": {
        ""artist"": ""Artist 2"",
          ""title"": ""Title 2""
     },
    ""49"": {
        ""artist"": ""Artist 50"",
          ""title"": ""Title 50""
     }
}";

var response = JsonConvert.DeserializeObject<SongsResponse>(json, new SongsResponseJsonConverter());

答案 1 :(得分:0)

可以使用常用方法反序列化JSON的常量部分,然后使用JSON2LINQ构造动态部分。在您的特定情况下,它看起来如下:

var json = JObject.Parse(responseString);

var response = json.ToObject<SongsResponse>();
response.songs = json.Properties().Where(t => Regex.Match(t.Name, "\\d+").Success).ToDictionary(t => t.Name, t => t.Value.ToObject<Song>());

注意:在第一次调用后,Regex.Match会缓存regex对象,并且所有后续调用都会重用该对象。