1

I'm writing a service to retrieve data from an API over HTTP from another team in my company. The JSON response body from their API looks a bit like this:

"SomeObject": {
    "SomeInnerObject": {
        "SomeProperty": {
            "Id": "123",
            "Type": "abc",
            "Name": "some value"
        }
    }
}

I'm writing a C# class to store the data in memory in order to do some comparisons. The nesting of the JSON object causes the class to look annoyingly repetitive:

public class MyClass 
{
    public SomeObjectModel SomeObject { get; set; }
    public class SomeObjectModel
    {
        public SomeInnerObjectModel InnerObject { get; set; }
        public class SomeInnerObjectModel
        {
            // etc...
        }
    }
}

I know for sure that the inner classes, like "SomeObjectModel", are only going to be read from and not instantiated elsewhere, so is there a way to combine the class definition and property definition lines into something more like this?

public class MyClass
{
    public SomeObject { get; set; } :
    {
        public SomeInnerObject { get; set; } :
        {
            // etc...
        }
    }
}

EDIT:

The JSON will have arrays in it, so take that into account if you are proposing an alternative using generics, etc.

Werns
  • 63
  • 1
  • 1
  • 7
  • 1
    Note that the classes declarations themselves don't need to be nested in order to use them in a nested manner. You could declare the `SomeInnerObject` class at the same level as `SomeObject` but still use `SomeInnerObject` inside `SomeObject`. – Brian Rogers Apr 12 '19 at 03:00

4 Answers4

7

If you're using this just to deserialize the JSON, you don't even need to define a class.. You can use a nested anonymous type.

First, create and populate (with dummy values) an anonymous type that has the properties you need to be able to read. Then pass it as the second parameter to DeserializeAnonymousType<T>(string,T).

The result is a new instance of the same anonymous type that you created, but now it's populated with values from the JSON.

var json = @"{'SomeObject': {'SomeInnerObject': {'SomeProperty': {'Id': '123','Type': 'abc','Name': 'some value'}}}}";

var template = new
{
    SomeObject = new
    {
        SomeInnerObject = new 
        { 
            SomeProperty = new 
            {
               Id = default(int),
               Type = default(string),
               Name = default(string)
            }
        }
    }
};

var result = JsonConvert.DeserializeAnonymousType(json, template);
var id = result.SomeObject.SomeInnerObject.SomeProperty.Id;
var type = result.SomeObject.SomeInnerObject.SomeProperty.Type;
var name = result.SomeObject.SomeInnerObject.SomeProperty.Name;

Console.WriteLine("{0} {1} {2}", id, type, name);

Output:

123 abc some value

See my working example on DotNetFiddle.

Edit: If your JSON contains an array, you can use new[] {} to create an array based on type inference and then put the anonymous types inside, like this:

var json = @"{ 'SomeObjects': [ { 'Id': '123', 'Name': 'some value' }, { 'Id': '456', 'Name': 'another value' } ]}";

var template = new
{
    SomeObjects = new [] { new { Id=default(int), Name=default(string)} }
};
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • I would've preferred it to be a class definition, but I'm not hardstruck on it and I imagine this might be my best option. – Werns Apr 11 '19 at 20:23
  • How would I create the template with JSON like this? "SomeObjects": [ { "Id": "123", "Name": "some value" }, { "Id": "456", "Name": "another value" }, ] – Werns Apr 11 '19 at 20:46
  • I updated my answer and the DotNetFiddle example to demonstrate how to handle that sort of JSON. – John Wu Apr 12 '19 at 04:29
2

The short answer is no, C# does not support any version of that syntactic sugar. The closest thing in C# is probably either anonymous types or value tuples.

Other languages have similar features:

  • C++ supports "inline classes" in declarations.
  • Java supports anonymous classes that are more sophisticated than C# anonymous record types.
  • Scala supports case classes which declare their members in the header of the declaration.

And so on. I think the first is the closest thing to what you are looking for.

Scala-style class declarations have been proposed for C# many times in the last decade, and may finally make it into C# 8; see https://blog.cdemi.io/whats-coming-in-c-8-0-records/

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
0

You can use the dynamic type. Here is a minimal example:

using Newtonsoft.Json;
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic x = JsonConvert.DeserializeObject("[{key: '1001', value: 'test'}, {key: '1002', value: 'test2'}, ]");
            Console.WriteLine(x[0].key);
            Console.WriteLine(x[0].value);
            Console.WriteLine(x[1].key);
            Console.WriteLine(x[1].value);
            Console.ReadLine();
        }
    }
}
RWRkeSBZ
  • 723
  • 4
  • 11
  • 2
    I'm not looking for a dynamic object, I'd like it to be explicitly typed. – Werns Apr 11 '19 at 20:20
  • A class is static by definition (as in "static typing"), so you cannot build a class on-the-fly. The purpose of `dynamic` is to address requirements just like yours. – RWRkeSBZ Apr 11 '19 at 20:23
  • Right, so I'm essentially asking for a non-static class that is explicitly typed, which I'm getting the feeling does not exist. – Werns Apr 11 '19 at 20:28
  • @Werns Correct. Because if you think about it, the type must be available at compilation for type-checking. The alternative is to use [tuple](https://learn.microsoft.com/en-us/dotnet/csharp/tuples). – RWRkeSBZ Apr 11 '19 at 20:32
0

You can create classes by using the paste special, paste JSON as classes.

https://channel9.msdn.com/Series/Windows-Store-Developer-Solutions/Quckly-Generate-C-Classes-from-JSON-Responses#time=01m56s

  • Very cool to know in the future, but currently the JSON from the external API is 2-3 times larger than the data I need, so it'd take more time to cut out what I don't need than to just make what I do need. – Werns Apr 12 '19 at 13:42