简体   繁体   中英

How to parse non-key JSON to C# classes?

I get a Json data from url as follows. But as you see, there are no key names in Json.

For example, "Flame Towers" is place name value but there is no any key name. Likewise, "2017-02-10" is date value, "The Lego Batman Movie 2D" is film name value but it declared as key and ["10:10"] is an array consists film session times.

I tried many class structurs for deserialize it to C# classes using JsonConvert.DeserializeObject<ClassName>(jsonString);

But every time it returns a null object. Also tried parse manually with JObject class and it seemed to me very confused.

So, can anyone help for true class structure parsing with JsonConvert class?

{
    {
        "Flame Towers": {
            "2017-02-10": {
                "The Lego Batman Movie 2D": [
                    "10:10"
                ],
                "Qatil 2D": [
                    "10:30"
                ],
                "Fifty Shades Darker 2D": [
                    "10:30",
                    "11:40",
                    "12:50",
                    "14:00",
                    "15:10",
                    "16:20",
                    "17:30",
                    "18:40",
                    "19:50",
                    "21:00",
                    "22:10",
                    "23:20",
                    "00:30",
                    "01:40"
                ],
                "John Wick: Chapter Two 2D": [
                    "11:00",
                    "12:10",
                    "13:20",
                    "14:30",
                    "15:40",
                    "16:50",
                    "18:00",
                    "20:20",
                    "21:30",
                    "22:40",
                    "23:50",
                    "01:00",
                    "02:10"
                ],
                "The Lego Batman Movie 3D": [
                    "11:00",
                    "12:10",
                    "13:00",
                    "14:10",
                    "15:00",
                    "17:00",
                    "19:00"
                ],
                "Ballerina 3D": [
                    "16:10"
                ],
                "Rings 2D": [
                    "17:55"
                ],
                "Ağanatiq 2D": [
                    "19:55"
                ],
                "Resident Evil: The Final Chapter 3D": [
                    "21:40",
                    "21:00",
                    "23:50",
                    "01:10"
                ],
                "The Great Wall 3D": [
                    "23:10"
                ]
            }
        },
        "Metro Park": {
            "2017-02-10": {
                "John Wick: Chapter Two 2D": [
                    "10:30",
                    "12:50",
                    "15:10",
                    "17:30",
                    "19:50",
                    "22:10",
                    "00:30"
                ],
                "Ağanatiq 2D": [
                    "10:00",
                    "11:50",
                    "13:40",
                    "15:30",
                    "17:20",
                    "19:10",
                    "21:00",
                    "23:00",
                    "00:50"
                ],
                "The Lego Batman Movie 2D": [
                    "10:30"
                ],
                "Fifty Shades Darker 2D": [
                    "11:00",
                    "13:20",
                    "15:40",
                    "18:00",
                    "20:20",
                    "02:00"
                ],
                "Hoqqa 2D": [
                    "11:10",
                    "12:50",
                    "14:30",
                    "16:10",
                    "17:50",
                    "19:30",
                    "21:10",
                    "22:50",
                    "00:30",
                    "02:10"
                ],
                "Naxox 2D": [
                    "11:20",
                    "13:10",
                    "15:00",
                    "16:50",
                    "18:40",
                    "20:30",
                    "22:20",
                    "00:10"
                ],
                "The Lego Batman Movie 3D": [
                    "12:30",
                    "14:30",
                    "16:30",
                    "18:30"
                ],
                "Ballerina 3D": [
                    "20:30"
                ],
                "Resident Evil: The Final Chapter 3D": [
                    "22:40",
                    "00:50"
                ],
                "The Great Wall 3D": [
                    "22:20",
                    "02:30"
                ],
                "Притяжение 3D": [
                    "00:20"
                ]
            }
        }
    }
}

There is a simple, hacky and speedy way of doing this. Just cut the first and the last { } symbols from the string before serializing.

if (jsonString.StartsWith("{{") && jsonString.EndsWith("}}"))
    jsonString = jsonString.Substring(2, jsonString.Length - 4);
JsonConvert.DeserializeObject<ClassName>(jsonString);

It looks like data from a collection of movie theatres and their active shows where the top item "Flame Towers" are the name of the cinema, the "2017-02-10" is the date and under is each show/movie and then their "display" time.

Knowing this, you can create a data structure that matches this.

... Something like this perhaps?

    public class Movie : IEnumerable<TimeSpan>
    {

        public Movie(string name, IReadOnlyList<TimeSpan> runTimes)
        {
            this.Name = name;
            this.RunTimes = runTimes;
        }

        public string Name { get; }

        public IReadOnlyList<TimeSpan> RunTimes { get; }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<TimeSpan> GetEnumerator()
        {
            return RunTimes.GetEnumerator();
        }

        public override string ToString()
        {
            return "[Movie] " + Name;
        }

        public static Movie Parse(JProperty data)
        {
            var name = data.Name;
            var runTimes = new List<TimeSpan>();

            foreach (var child in data.Values())
            {
                runTimes.Add(TimeSpan.Parse(child.Value<string>()));
            }

            return new Movie(name, runTimes);
        }
    }


    public class MovieCollectionDate : IEnumerable<Movie>
    {

        public MovieCollectionDate(DateTime date, IReadOnlyList<Movie> movies)
        {
            this.Date = date;
            this.Movies = movies;
        }

        public DateTime Date { get; }

        public IReadOnlyList<Movie> Movies { get; }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<Movie> GetEnumerator()
        {
            return this.Movies.GetEnumerator();
        }

        public override string ToString()
        {
            return "[Date] " + Date + " - " + Movies.Count + " show(s)";
        }

        public static MovieCollectionDate Parse(JProperty data)
        {
            var date = DateTime.Parse(data.Name);
            var movies = new List<Movie>();

            foreach (var upperChild in data.Children<JObject>())
            {
                foreach (var child in upperChild.Children())
                {
                    movies.Add(Movie.Parse(child as JProperty));
                }
            }

            return new MovieCollectionDate(date, movies);
        }
    }

    public class MovieTheatre : IEnumerable<MovieCollectionDate>
    {

        public MovieTheatre(string name, IReadOnlyList<MovieCollectionDate> dateAndMovies)
        {
            this.Name = name;
            this.DateAndMovies = dateAndMovies;
        }

        public string Name { get; }
        public IReadOnlyList<MovieCollectionDate> DateAndMovies { get; }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<MovieCollectionDate> GetEnumerator()
        {
            return this.DateAndMovies.GetEnumerator();
        }

        public override string ToString()
        {
            return "[Theatre] " + Name + " - " + DateAndMovies.Count + " open day(s)";
        }


        public static MovieTheatre Parse(JProperty data)
        {
            var name = data.Name;
            var movieCollectionDates = new List<MovieCollectionDate>();
            foreach (var upperChild in data.Children<JObject>())
            {
                foreach (var child in upperChild.Children())
                {
                    movieCollectionDates.Add(MovieCollectionDate.Parse(child as JProperty));
                }
            }

            return new MovieTheatre(name, movieCollectionDates);
        }
    }

    public class MovieTheatreCollection : IEnumerable<MovieTheatre>
    {

        public MovieTheatreCollection(IReadOnlyList<MovieTheatre> movieTheatres)
        {
            this.MovieTheatres = movieTheatres;
        }

        public IReadOnlyList<MovieTheatre> MovieTheatres { get; }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<MovieTheatre> GetEnumerator()
        {
            return this.MovieTheatres.GetEnumerator();
        }

        public override string ToString()
        {
            return "MovieTheatreCollection: Containing " + MovieTheatres.Count + " movie theatre(s)";
        }

        public static MovieTheatreCollection Parse(JObject data)
        {
            var theatres = new List<MovieTheatre>();
            foreach (var child in data.Children().Cast<JProperty>())
            {
                theatres.Add(MovieTheatre.Parse(child));
            }
            return new MovieTheatreCollection(theatres);
        }
    }

this is obviously not the most elegant way of solving the problem. But seeing as this "key-less" json wouldnt just Deserialize properly without some sort of hack. Creating a data structure that matches your needs (more manual work unfortunately) will at least work ;)

You can use the code above with the following code:

JObject obj = JObject.Parse(... the json string you had above ...)

MovieTheatreCollection movieTheatres = MovieTheatreCollection.Parse(obj);

foreach (var movieTheatre in movieTheatres)
{
    Console.WriteLine(movieTheatre);
    foreach (var openDay in movieTheatre)
    {
        Console.WriteLine("   " + openDay);
        foreach (var movie in openDay)
        {
            Console.WriteLine("      " + movie);
            foreach (var runtime in movie) Console.WriteLine("        - " + runtime);
        }
    }                
}

Finally, just like 'Just Shadow' mentioned in the answer above, the json is malformed and contains extra curly brackets that needs to be removed or the object wont parse properly.

An ugly, but reasonably compact way to parse this would be:

static void Main(string[] args)
    {                   
        var jo = JObject.Parse(File.ReadAllText("data.json").Trim('{').Trim('}'));
        foreach (var place in jo)
        {
            Console.WriteLine($"Place: {place.Key}");
            foreach (var dateOrMovie in place.Value.Children<JProperty>())
            {
                Console.WriteLine($"\tDate: {dateOrMovie.Name}");
                var movies = dateOrMovie.Children<JObject>().First().Children<JProperty>();
                foreach (var movie in movies)
                {
                    Console.WriteLine($"\t\t{movie.Name}");
                    foreach (JValue time in movie.Children<JArray>().First())
                    {
                        Console.WriteLine($"\t\t\t{time.Value}");
                    }
                }
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM