-2

I have following JSON file:

[
    {"sport": "CYCLING_SPORT"},
    {"source": "TRACK_MOBILE"},
    {"created_date": "2016-03-28 12:00:00.0"},
    {"start_time": "2016-03-28 12:00:00.0"},
    {"end_time": "2016-03-28 13:00:00.0"},
    {"duration_s": 3600},
    {"distance_km": 10.123412312412},
    {"calories_kcal": 1000.12},
    {"altitude_min_m": 100.5},
    {"altitude_max_m": 200.5},
    {"speed_avg_kmh": 30.21314151234123},
    {"speed_max_kmh": 50.1234},
    {"hydration_l": 1.142124},
    {"ascend_m": 200.9},
    {"descend_m": 200.9},
    {"points": [
        [
            {"location": [[
                {"latitude": 40.1234},
                {"longitude": 40.1234}
            ]]},
            {"distance_km": 0},
            {"timestamp": "Mon Mar 28 12:00:00 UTC 2016"}
        ],
        [
            {"location": [[
                {"latitude": 50.1234},
                {"longitude": 50.1234}
            ]]},
            {"distance_km": 10.1234},
            {"timestamp": "Mon Mar 28 13:00:00 UTC 2016"}
        ]
    ]}
]

I generated classes to hold data:

public class Workout
{
    public string sport { get; set; }
    public string source { get; set; }
    public string created_date { get; set; }
    public string start_time { get; set; }
    public string end_time { get; set; }
    public int duration_s { get; set; }
    public double? distance_km { get; set; }
    public double? calories_kcal { get; set; }
    public double? altitude_min_m { get; set; }
    public double? altitude_max_m { get; set; }
    public double? speed_avg_kmh { get; set; }
    public double? speed_max_kmh { get; set; }
    public double? hydration_l { get; set; }
    public double? ascend_m { get; set; }
    public double? descend_m { get; set; }
    public Point[][] points { get; set; }
}

public class Point
{
    public Location[][] location { get; set; }
    public double? distance_km { get; set; }
    public string? timestamp { get; set; }
    public double? altitude { get; set; }
    public double? speed_kmh { get; set; }
}

public class Location
{
    public double latitude { get; set; }
    public double longitude { get; set; }
}

I'm trying to parse JSON using following code:

using System;
using System.Text.Json;

namespace Reader
{
    class Program
    {
        static void Main(string[] args)
        {
            var json = System.IO.File.ReadAllText(@"<path_to_file>");
            var workout = JsonSerializer.Deserialize<Workout[]>(json)[0];
            System.Console.Write(workout.sport);
            System.Console.Write(workout.points.Length);
        }
    }
}

Unfortunately, this is what I get while I'm debugging (and of course null reference exception): enter image description here

One value looks good, but everything else is empty. Can someone give me a hint what I am doing wrong?

EDIT:

I can't edit JSON directly, but I can modify it before deserializing. So I did following:

var builder = new StringBuilder(System.IO.File.ReadAllText(@"<path_to_file>"));
builder.Replace("{", "");
builder.Replace("}", "");
builder[0] = '{';
builder[builder.Length - 1] = '}';
var json = builder.ToString();
System.Console.Write(json);

var workout = JsonSerializer.Deserialize<Workout>(json);
System.Console.Write(workout.sport);
System.Console.Write(workout.points.Length);

And now I get this:

{
    "sport": "CYCLING_SPORT",
    "source": "TRACK_MOBILE",
    "created_date": "2016-03-28 12:00:00.0",
    "start_time": "2016-03-28 12:00:00.0",
    "end_time": "2016-03-28 13:00:00.0",
    "duration_s": 3600,
    "distance_km": 10.123412312412,
    "calories_kcal": 1000.12,
    "altitude_min_m": 100.5,
    "altitude_max_m": 200.5,
    "speed_avg_kmh": 30.21314151234123,
    "speed_max_kmh": 50.1234,
    "hydration_l": 1.142124,
    "ascend_m": 200.9,
    "descend_m": 200.9,
    "points": [
        [
            "location": [[
                "latitude": 40.1234,
                "longitude": 40.1234
            ]],
            "distance_km": 0,
            "timestamp": "Mon Mar 28 12:00:00 UTC 2016"
        ],
        [
            "location": [[
                "latitude": 50.1234,
                "longitude": 50.1234
            ]],
            "distance_km": 10.1234,
            "timestamp": "Mon Mar 28 13:00:00 UTC 2016"
        ]
    ]
}

But deserializer is still not happy:

System.Text.Json.JsonException: 'The JSON value could not be converted to Point[]. Path: $.points[0][0] | LineNumber: 18 | BytePositionInLine: 22.'
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
serwus
  • 155
  • 1
  • 14
  • You have an array of objects in the json. You deserialize it to an array objects, which is correct. (Each object would have only one of the possible properties set.) You then take only the first object (the result of parsing the `{"sport": "CYCLING_SPORT"}`) and discard the rest, so naturally that first object only contains the only property that was the first object in the json array. If you meant to merge the array of objects into one, that has all the properties, then do that instead of discarding the other objects. – GSerg Jan 03 '21 at 21:35
  • @GSerg the problem is the "points" array-of-arrays being deserialized as null, as well as all the other objects. – CodeCaster Jan 03 '21 at 21:38
  • So I deserialized it into many objects instead of one? So my code structure for holding data is wrong? – serwus Jan 03 '21 at 21:40
  • 2
    @serwus Yes you have, but your json is many objects in the first place. The class `Workout` may be used to hold this kind of data, but if you put it there manually from all the individual small objects. You should either change your json so that all the properties belong to the same one object, or deserialize the current json into some sort of a dictionary where you look up the property value by its string name. – GSerg Jan 03 '21 at 21:44
  • Well, yes, the code is wrong, but the JSON as well. Not incorrect JSON wrong, but definitely inappropriately designed. Way too much nesting. – CodeCaster Jan 03 '21 at 21:44
  • To be clear: I didn't create that JSON, I'm just trying to deserialize it. It comes from recently closed endomondo. :) – serwus Jan 03 '21 at 22:07
  • `var builder = new StringBuilder(System.IO.File.ReadAllText(@"")); builder.Replace("{", ""); builder.Replace("}", "");` I don't believe this is a good idea since `"location": [[ "latitude": 40.1234, "longitude": 40.1234 ]],` is not valid JSON. – mjwills Jan 03 '21 at 22:33
  • It indeed generates code for oryginal JSON, but it doesn't change anything. Sport field is readable, others are nulls. – serwus Jan 03 '21 at 22:45
  • Hmm, not sure how to achive that. Do you mean editing generated code to use JsonSerializer? – serwus Jan 03 '21 at 22:51
  • Ignore me, JSON.NET doesn't handle it properly either. Did you try the below `KeyValurPair` suggestion? – mjwills Jan 03 '21 at 22:52
  • it's not JSON. It doesn't validate. – MikeJ Jan 03 '21 at 23:28

1 Answers1

3

Everything in the json that is a key/string pair, key/number pair or key/datetime pair should not be nested in {} - it should just be at the root level, i.e.

[{
    "sport": "CYCLING_SPORT",
    ...
},
{
    ...
}]

If you can't change the json, change your C# code to have KeyValurPair<string, string> or KeyValuePair<string, int> or KeyValuePair<string, double> or KeyValuePair<string, DateTimeOffset> values for those elements.

In short, it looks like this json has a lot of redundant nesting of objects that is not needed.

jjxtra
  • 20,415
  • 16
  • 100
  • 140