1

All,

Edit: Firstly thanks for everyone's help. Secondly I'm new to Stack Overflow so apologises if I've added this edit incorrectly.

Following the commments and replies I've updated my class structure to:

services class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;

namespace RTT_API
{
    class services
    {
        public List<service> service = new List<service>();

        public services()
        {

        }
    }
}

Service class:


using System;
using System.Collections.Generic;
using System.Text;

namespace RTT_API
{
    class service
    {
        public string atocCode{get; set;}

        public service()
        {

        }
    }
}

Unfortunately I'm still getting the same error. I think I still haven't quite matched my class structure to the JSON structure? Unfortunately I'm not sure where my mistake is. If it helps to highlight my mistake using a comparison then the following works:

Location class

using System; 
using System.Collections.Generic;
using System.Text;

namespace RTT_API
{
    class location
    {
        public string name { get; set; }
        public string crs { get; set; }

        public location()
        {
            
        }
    }
}

Location deserilisation command and test output:

location locations = JsonSerializer.Deserialize<location>(channelResponse.RootElement.GetProperty("location").GetRawText());

MessageBox.Show(locations.crs);

Original question:

My JSON is as follows:

{
   "location": {
       "name": "Bournemouth",
       "crs": "BMH",
       "tiploc": "BOMO"
   },
   "filter": null,
   "services": [
       {
           "locationDetail": {
               "realtimeActivated": true,
               "tiploc": "BOMO",
               "crs": "BMH",
               "description": "Bournemouth",
               "wttBookedArrival": "011630",
               "wttBookedDeparture": "011830",
               "gbttBookedArrival": "0117",
               "gbttBookedDeparture": "0118",
               "origin": [
                   {
                       "tiploc": "WATRLMN",
                       "description": "London Waterloo",
                       "workingTime": "230500",
                       "publicTime": "2305"
                   }
               ],
               "destination": [
                   {
                       "tiploc": "POOLE",
                       "description": "Poole",
                       "workingTime": "013000",
                       "publicTime": "0130"
                   }
               ],
               "isCall": true,
               "isPublicCall": true,
               "realtimeArrival": "0114",
               "realtimeArrivalActual": false,
               "realtimeDeparture": "0118",
               "realtimeDepartureActual": false,
               "platform": "3",
               "platformConfirmed": false,
               "platformChanged": false,
               "displayAs": "CALL"
           },
           "serviceUid": "W90091",
           "runDate": "2013-06-11",
           "trainIdentity": "1B77",
           "runningIdentity": "1B77",
           "atocCode": "SW",
           "atocName": "South West Trains",
           "serviceType": "train",
           "isPassenger": true
       }
   ]
}

My class structure is as follows:

servicelist class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;

namespace RTT_API
{
    class servicelist
    {
        public List<services> service = new List<services>();

        public servicelist()
        {

        }
    }
}

services class:

using System;
using System.Collections.Generic;
using System.Text;

namespace RTT_API
{
    class services
    {
        public int serviceUid;

        public services()
        {

        }

    }
}

For deserialisation I have tried:

services servicelist = JsonSerializer.Deserialize<services>(channelResponse.RootElement.GetProperty("services").GetRawText());

and

servicelist servicelist = JsonSerializer.Deserialize<servicelist>(channelResponse.RootElement.GetProperty("services").GetRawText());;

In both cases I get 'System.Text.Json.JsonException'

I think there is a mismatch betwee the class structure and the JSON but I can't work what the problem is? It's the first time I've tried to desarialise an array.

Thanks

using System; 
using System.Collections.Generic;
using System.Text;

namespace RTT_API
{
    class location
    {
        public string name { get; set; }
        public string crs { get; set; }

        public location()
        {
            
        }
    }
}

4 Answers 4

1

You can generate exact C# classes according to your JSON using tools for exactly that purpose. I used https://json2csharp.com/ , another is https://jsonutils.com/ - these are web services and don't require installation on computer, another option is generating classes through Visual Studio (with Web Essentials installed), there you would use Edit - Paste special - paste JSON as class.

Once you have the valid classes (I pasted generated classes below) you can deserialize entire Root object and then access any part of it, including services part:

    // jsonInputText holds entire JSON string you posted
    Root root = JsonSerializer.Deserialize<Root>(jsonInputText);
    List<Service> serviceList = root.services;

Generated classes:

public class Location
{
    public string name { get; set; }
    public string crs { get; set; }
    public string tiploc { get; set; }
}

public class Origin
{
    public string tiploc { get; set; }
    public string description { get; set; }
    public string workingTime { get; set; }
    public string publicTime { get; set; }
}

public class Destination
{
    public string tiploc { get; set; }
    public string description { get; set; }
    public string workingTime { get; set; }
    public string publicTime { get; set; }
}

public class LocationDetail
{
    public bool realtimeActivated { get; set; }
    public string tiploc { get; set; }
    public string crs { get; set; }
    public string description { get; set; }
    public string wttBookedArrival { get; set; }
    public string wttBookedDeparture { get; set; }
    public string gbttBookedArrival { get; set; }
    public string gbttBookedDeparture { get; set; }
    public List<Origin> origin { get; set; }
    public List<Destination> destination { get; set; }
    public bool isCall { get; set; }
    public bool isPublicCall { get; set; }
    public string realtimeArrival { get; set; }
    public bool realtimeArrivalActual { get; set; }
    public string realtimeDeparture { get; set; }
    public bool realtimeDepartureActual { get; set; }
    public string platform { get; set; }
    public bool platformConfirmed { get; set; }
    public bool platformChanged { get; set; }
    public string displayAs { get; set; }
}

public class Service
{
    public LocationDetail locationDetail { get; set; }
    public string serviceUid { get; set; }
    public string runDate { get; set; }
    public string trainIdentity { get; set; }
    public string runningIdentity { get; set; }
    public string atocCode { get; set; }
    public string atocName { get; set; }
    public string serviceType { get; set; }
    public bool isPassenger { get; set; }
}

public class Root
{
    public Location location { get; set; }
    public object filter { get; set; }
    public List<Service> services { get; set; }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your help. I appreciate the info on the online tools. I would be interested in learning why my definition didn't work if possible. I updated my serviceUID definition to public string serviceUid { get; set; } but it still hasn't worked.
No problem. Classes must match JSON structure. In JSON string under property "services" you have a JSON array - this means that in class property "services" must be a collection (List<>) of "service" objects, these "service" objects are presented by generated class Service. Service object needs to have certain properties, such as "locationDetail" etc., once you implement objects and collections consistently there should be no error - easiest way to create correct class is to use some of the JSON-to-class generators. As long as class structure doesn't match there might be errors.
Thanks again. I updated my code and question. I'm new to StackOverflow so hopefully that was the correct thing to do.
This is the best way to go; many thanks for the tips!
0

If you need to deserialize only just a part of your json then you can use the JObject and JToken helper classes for that.

var json = File.ReadAllText("Sample.json");
JObject topLevelObject = JObject.Parse(json);
JToken servicesToken = topLevelObject["services"];
var services = servicesToken.ToObject<List<Service>>();
  • The topLevelObject contains the whole json in a semi-parsed format.
  • You can use the indexer operator to retrieve an object / array by using one of the top level keys.
  • On a JToken you can call the ToObject<T> to deserialize the data into a custom data class.

In order to be able to parse your json I had to adjust the services type because the W90091 as serviceUid can't be parsed as int. So here is my Service class definition:

public class Service
{
    public string ServiceUid;
}

One thing to note here is that casing does not matter in this case so please use CamelCasing in your domain models as you would normally do in C#.

1 Comment

Thanks for the advice. It's useful.
0

Thanks for everyone's help.

Firstly I had to make a few changes to the class names as they didn't match the JSON. I also had to change the syntax of two commands which I've detailed below:

I changed the definition of the list of objects from:

public List<services> service = new List<services>();

to:

public List<service> destination { get; set; };

and deserilisation command from:

services servicelist = JsonSerializer.Deserialize<services>(channelResponse.RootElement.GetProperty("services").GetRawText());

to

var servicelist = JsonSerializer.Deserialize<List<service>>(channelResponse.RootElement.GetProperty("services").GetRawText());

The change from services to var might not be the best solution. I think it's the first change, and matching the class names to the JSON, that fundamentally fixed the issue.

Comments

0

Solution when using Newtonsoft to deserialize JSON.

class services : List<service> 
{ 
}

Then use JsonConvert.DeserializeObject() to deserialize.

When Json is of Array type then the wrapper class should be inherited from ICollection/IList type.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.