Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize JSON with number objects in root

Tags:

c#

json.net

I receive the following JSON:

{
"1": {
    "startDate": "",
    "endDate": "",
    "projectId": 10000,
    "build": "",
    "totalExecutions": 1,
    "totalExecuted": 1,
    "environment": "",
    "description": "Audit Test Cycle",
    "executionSummaries": {
        "executionSummary": [
            {
                "count": 0,
                "statusKey": -1,
                "statusName": "UNEXECUTED",
                "statusColor": "#A0A0A0",
                "statusDescription": "The test has not yet been executed."
            },
            {
                "count": 1,
                "statusKey": 1,
                "statusName": "PASS",
                "statusColor": "#75B000",
                "statusDescription": "Test was executed and passed successfully."
            },
            {
                "count": 0,
                "statusKey": 2,
                "statusName": "FAIL",
                "statusColor": "#CC3300",
                "statusDescription": "Test was executed and failed."
            },
            {
                "count": 0,
                "statusKey": 3,
                "statusName": "WIP",
                "statusColor": "#F2B000",
                "statusDescription": "Test execution is a work-in-progress."
            },
            {
                "count": 0,
                "statusKey": 4,
                "statusName": "BLOCKED",
                "statusColor": "#6693B0",
                "statusDescription": "The test execution of this test was blocked for some reason."
            }
        ]
    },
    "name": "Audit Test Cycle",
    "expand": "executionSummaries",
    "versionId": 10000,
    "started": ""
},
"2":  {
    "startDate": "",
    "endDate": "",
    "projectId": 10000,
    "build": "",
    "totalExecutions": 1,
    "totalExecuted": 1,
    "environment": "",
    "description": "Audit Test Cycle",
    "executionSummaries": {
        "executionSummary": [
            {
                "count": 0,
                "statusKey": -1,
                "statusName": "UNEXECUTED",
                "statusColor": "#A0A0A0",
                "statusDescription": "The test has not yet been executed."
            },
            {
                "count": 1,
                "statusKey": 1,
                "statusName": "PASS",
                "statusColor": "#75B000",
                "statusDescription": "Test was executed and passed successfully."
            },
            {
                "count": 0,
                "statusKey": 2,
                "statusName": "FAIL",
                "statusColor": "#CC3300",
                "statusDescription": "Test was executed and failed."
            },
            {
                "count": 0,
                "statusKey": 3,
                "statusName": "WIP",
                "statusColor": "#F2B000",
                "statusDescription": "Test execution is a work-in-progress."
            },
            {
                "count": 0,
                "statusKey": 4,
                "statusName": "BLOCKED",
                "statusColor": "#6693B0",
                "statusDescription": "The test execution of this test was blocked for some reason."
            }
        ]
    },
    "name": "Audit Test Cycle",
    "expand": "executionSummaries",
    "versionId": 10000,
    "started": ""
},
"3":  {
    "startDate": "",
    "endDate": "",
    "projectId": 10000,
    "build": "",
    "totalExecutions": 1,
    "totalExecuted": 1,
    "environment": "",
    "description": "Audit Test Cycle",
    "executionSummaries": {
        "executionSummary": [
            {
                "count": 0,
                "statusKey": -1,
                "statusName": "UNEXECUTED",
                "statusColor": "#A0A0A0",
                "statusDescription": "The test has not yet been executed."
            },
            {
                "count": 1,
                "statusKey": 1,
                "statusName": "PASS",
                "statusColor": "#75B000",
                "statusDescription": "Test was executed and passed successfully."
            },
            {
                "count": 0,
                "statusKey": 2,
                "statusName": "FAIL",
                "statusColor": "#CC3300",
                "statusDescription": "Test was executed and failed."
            },
            {
                "count": 0,
                "statusKey": 3,
                "statusName": "WIP",
                "statusColor": "#F2B000",
                "statusDescription": "Test execution is a work-in-progress."
            },
            {
                "count": 0,
                "statusKey": 4,
                "statusName": "BLOCKED",
                "statusColor": "#6693B0",
                "statusDescription": "The test execution of this test was blocked for some reason."
            }
        ]
    },
    "name": "Audit Test Cycle",
    "expand": "executionSummaries",
    "versionId": 10000,
    "started": ""
},
"recordsCount": 3}

}

One of the root object is recordcount, but the others are numbers. These numbers are important, so I can't loose them. The number is not incrementing with 1, it can be 8,5,2,4,6 or any other combination (but unique!).

I want to deserialize them into the following classes below, but don't know how to process the number.

public class Cycles //: Dictionary<string,Cycle>
{
    public Dictionary<string,Cycle> cycles { get; set; }
    public int recordsCount { get; set; } 
}


public class Cycle 
{
    public int totalExecutions { get; set; }
    public string endDate { get; set; }
    public string description { get; set; }
    public int totalExecuted { get; set; }
    public string started { get; set; }
    public string versionName { get; set; }
    public string expand { get; set; }
    public string projectKey { get; set; }
    public int versionId { get; set; }
    public string environment { get; set; }
    public string build { get; set; }
    public string createdBy { get; set; }
    public string ended { get; set; }
    public string name { get; set; }
    public string modifiedBy { get; set; }
    public int projectId { get; set; }
    public string createdByDisplay { get; set; }
    public string startDate { get; set; }
}

I know the JSON is not nice, but I can't change that. It's from a third party tool.

Normally I would just do

return _jsonDeserializer.Deserialize<Cycles>(response);

But that doesn't work. So I tried not to "deserialize" it with:

Cycles result = new Cycles();
        JObject jsonOb = JObject.Parse(response.Content);
        result.recordsCount = jsonOb.Value<int>("recordsCount");
        jsonOb.Remove("recordsCount");
        var values = jsonOb.ToObject<Dictionary<string,Cycle>>();
        result.cycles = values;

I can get the "recordscount" data, but when trying to create the Dictionary I receive the error

error CS0119: 'Dictionary<string, Cycle>' is a type, which is not valid in the given contex
like image 729
DavidH Avatar asked Feb 20 '26 19:02

DavidH


1 Answers

If we didn't have the recordsCount property in the JSON, it would be reasonably simple. As it is, I suspect the simplest approach is to using LINQ to JSON first, work out which values belong in the dictionary, and ask JSON.NET to convert each of those... but populate the top-level object yourself. (This is much like what you were already trying, by the looks of it.)

Here's an example that works with your sample data and classes:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;

class Test
{
    static void Main()
    {
        var json = File.ReadAllText("test.json");
        var parsed = JObject.Parse(json);
        var cycles = new Cycles
        {
            cycles = parsed.Properties()
                .Where(p => int.TryParse(p.Name, out _))
                .ToDictionary(p => p.Name, p => p.Value.ToObject<Cycle>()),                  
            recordsCount = (int) parsed["recordsCount"]
        };
        Console.WriteLine(string.Join(", ", cycles.cycles.Keys));
    }
}

As an aside, I'd advise renaming all of your properties to idiomatic C# ones, and use [JsonProperty] to specify the name used in JSON.

like image 129
Jon Skeet Avatar answered Feb 23 '26 08:02

Jon Skeet