将JSON字符串反序列化为C#类

时间:2017-03-19 11:04:01

标签: c# json api uwp

好的,我会尽量做到简短而具体。我从一个公共API获取一个JSON字符串,如下所示:(简短版本,只拍了2个样本)

[{
    "$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
    "id": "bakerloo",
    "name": "Bakerloo",
    "modeName": "tube",
    "disruptions": [],
    "created": "2017-03-16T15:56:01.01Z",
    "modified": "2017-03-16T15:56:01.01Z",
    "lineStatuses": [
      {
        "$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
        "id": 0,
        "statusSeverity": 10,
        "statusSeverityDescription": "Good Service",
        "created": "0001-01-01T00:00:00",
        "validityPeriods": []
      }
    ],
    "routeSections": [],
    "serviceTypes": [
      {
        "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
        "name": "Regular",
        "uri": "/Line/Route?ids=Bakerloo&serviceTypes=Regular"
      }
    ],
    "crowding": {
      "$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities"
    }
  },
  {
    "$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
    "id": "central",
    "name": "Central",
    "modeName": "tube",
    "disruptions": [],
    "created": "2017-03-16T15:56:01.01Z",
    "modified": "2017-03-16T15:56:01.01Z",
    "lineStatuses": [
      {
        "$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
        "id": 0,
        "statusSeverity": 10,
        "statusSeverityDescription": "Good Service",
        "created": "0001-01-01T00:00:00",
        "validityPeriods": []
      }
    ],
    "routeSections": [],
    "serviceTypes": [
      {
        "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
        "name": "Regular",
        "uri": "/Line/Route?ids=Central&serviceTypes=Regular"
      },
      {
        "$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
        "name": "Night",
        "uri": "/Line/Route?ids=Central&serviceTypes=Night"
      }
    ],
    "crowding": {
      "$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities"
    }
  }]

到目前为止一直很好,下面我将粘贴我正在使用的相关代码,并尝试将此JSON字符串反序列化为我从json2csharp免费在线服务获得的C#类。我试图实现此目的的相关代码是:

public async static Task<tubeStatusRootObject> GetTubeStatus(string url)
{
    var http = new HttpClient();
    var response = await http.GetAsync(url);
    var result = await response.Content.ReadAsStringAsync(); //This is working
    var deserializer = new DataContractJsonSerializer(typeof(tubeStatusRootObject));
    var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
    var data = (tubeStatusRootObject)deserializer.ReadObject(ms);
    return data; //all "data" properties are null
}

你可以阅读上面的评论,所有我在返回之前获取内部数据都是空的。

json2csharp生成的类如下所示:

[DataContract]
public class Disruption
{
    [DataMember]
    public string type { get; set; }

    [DataMember]
    public string category { get; set; }

    [DataMember]
    public string categoryDescription { get; set; }

    [DataMember]
    public string description { get; set; }

    [DataMember]
    public string additionalInfo { get; set; }

    [DataMember]
    public string created { get; set; }

    [DataMember]
    public List<object> affectedRoutes { get; set; }

    [DataMember]
    public List<object> affectedStops { get; set; }

    [DataMember]
    public string closureText { get; set; }

    [DataMember]
    public bool? isWholeLine { get; set; }
}

[DataContract]
public class LineStatus
{
    [DataMember]
    public string type { get; set; }

    [DataMember]
    public int id { get; set; }

    [DataMember]
    public int statusSeverity { get; set; }

    [DataMember]
    public string statusSeverityDescription { get; set; }

    [DataMember]
    public string created { get; set; }

    [DataMember]
    public List<object> validityPeriods { get; set; }

    [DataMember]
    public string lineId { get; set; }

    [DataMember]
    public string reason { get; set; }

    [DataMember]
    public Disruption disruption { get; set; }
}

[DataContract]
public class ServiceType
{
    [DataMember]
    public string type { get; set; }

    [DataMember]
    public string name { get; set; }

    [DataMember]
    public string uri { get; set; }
}

[DataContract]
public class Crowding
{
    [DataMember]
    public string type { get; set; }
}

[DataContract]
public class tubeStatusRootObject
{
    [DataMember]
    public string type { get; set; }

    [DataMember]
    public string id { get; set; }

    [DataMember]
    public string name { get; set; }

    [DataMember]
    public string modeName { get; set; }

    [DataMember]
    public List<object> disruptions { get; set; }

    [DataMember]
    public string created { get; set; }

    [DataMember]
    public string modified { get; set; }

    [DataMember]
    public List<LineStatus> lineStatuses { get; set; }

    [DataMember]
    public List<object> routeSections { get; set; }

    [DataMember]
    public List<ServiceType> serviceTypes { get; set; }

    [DataMember]
    public Crowding crowding { get; set; }
}

显然我刚刚添加了[DataContract]&{39}和[DataMember]。任何人都知道我做错了什么,可以帮助我

我已按照Channel9

中的示例进行操作

请不要标记重复,因为我发现了很多类似的问题,有些使用了newtonsoft json,但是我无法将解决方案实现到我的示例中

3 个答案:

答案 0 :(得分:2)

提供的示例数据描述了一个数组(tubeStatusRootObject[])但是当您尝试反序列化时,将其强制转换为单个实例,这是一个无效的强制转换。这就是datanull的原因。

如果有可用于解决问题的工具,也无需重新发明轮子。

static http = new HttpClient(); //reuse httpclient instead of creating a new one each time.
public async static Task<tubeStatusRootObject[]> GetTubeStatus(string url) {
    var response = await http.GetAsync(url); 
    var json = await response.Content.ReadAsStringAsync(); //This is working
    var data = Newtonsoft.Json.JsonConvert.DeserializeObject<tubeStatusRootObject[]>(json);
    return data;
}

答案 1 :(得分:1)

这是一个有点复杂的对象。

当它反序列化时(你现在正在做的方式),它会查找与预期数据类型相同的对象的匹配名称。如果找不到,则反序列化失败并返回null。而且非常肯定。

如果不是newtonsoft,您可以将每个嵌套对象的数据类型与某些泛型对象匹配。或者需要执行一些字符串操作来自行反序列化(非常复杂)。

我更喜欢将Newtonsoft.json用于此类对象

答案 2 :(得分:1)

正如其他人所说的那样,你需要反序列化为一个数组,而不仅仅是你定义的一个类型的实例,因为响应是一个数组。

如果您将响应读入字符串,那么Json.Net意味着您只需要一个单行回复

var data= Newtonsoft.Json.JsonConvert.DeserializeObject<tubeStatusRootObject[]>(result);

使用DataContractJsonSerializer

时,将其与3 +行进行比较
var deserializer = new DataContractJsonSerializer(typeof(tubeStatusRootObject[]));
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(result)))
{
    var data = (tubeStatusRootObject[])deserializer.ReadObject(ms);
}

(注意使用using包装器来确保释放MemoryStream。)

如果您直接将HTTP响应流读入解串器,则无法获得LoC保存。

using (var s = http.GetStreamAsync(url).Result)
using (var sr = new StreamReader(s))
using (var reader = new Newtonsoft.Json.JsonTextReader(sr))
{
    var serializer = new Newtonsoft.Json.JsonSerializer();
    var data = serializer.Deserialize<tubeStatusRootObject[]>(reader);
}

对战

using (var stream = await response.Content.ReadAsStreamAsync())
{
    var dcjs = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(tubeStatusRootObject[]));

    var data= (tubeStatusRootObject[])dcjs.ReadObject(stream);
}

您可能想要考虑的另一件事是性能。 Json.Net claim the following

Json Serializers performance comparison graph