Why generate C# classes from a sample?
C# wants its model classes up front, and a real API response
does not care: it hands you camelCase keys, nested objects,
arrays of things that only sometimes have all their fields,
and the occasional value that is null until it isn't. Writing
the DTOs by hand means transcribing all of that into
PascalCase properties, deciding which ones must be nullable,
and remembering to pin every renamed property to its wire
name. The sample already contains those decisions as facts.
This converter reads them off mechanically: objects become
classes, arrays become List<T>, absence
and null become ? on value types, and renames
get their [JsonPropertyName] attributes written
for you — with the judgement calls listed, not hidden.
How to use this tool
- Paste the JSON. A real API response is ideal; include several array elements if you have them, because absence across elements is how nullable properties are discovered.
- Name the root class.
Rootis fine for a quick look; for code you will keep, use the name it will wear in your project. - Press Generate C#. The root class comes first, the classes it references after it.
- Read the notes if any appear. They list every slot that became
object, every reference type whose key sometimes goes missing, and every name collision — nothing was decided silently. - Copy or download the result, then refine it: narrow
longwhere your domain knows better, swap inDateTimeOffsetorGuidwhere strings are really those, and let the compiler take it from there.
The rules it follows
Numbers. Whole numbers become
long; any fraction widens the slot to
double — a JSON 1.0 is the number 1
to every parser, so a field your API always sends as
2.0 reads as an integer sample unless a real
fraction appears somewhere. Names. Classes
and properties are PascalCase; whenever that changes the
spelling, a [JsonPropertyName] pins the original
key, and the System.Text.Json.Serialization
using appears only when at least one attribute does.
Absence and null. Missing from some
elements or sometimes null → long? /
double? / bool? for value types;
reference types stay plain and the absence is noted.
Null everywhere or genuinely mixed types → object,
always noted. Merging. Identical shapes —
same keys, same types — share one class instead of being
emitted twice.
Worked example
Given this sample (the second user has no nickname, and the keys are camelCase):
{
"teamName": "Platform",
"users": [
{ "id": 1, "firstName": "Ada", "nickname": "ada" },
{ "id": 2, "firstName": "Grace" }
]
} the converter emits:
using System.Collections.Generic;
using System.Text.Json.Serialization;
public class Root
{
[JsonPropertyName("teamName")]
public string TeamName { get; set; }
[JsonPropertyName("users")]
public List<User> Users { get; set; }
}
public class User
{
[JsonPropertyName("id")]
public long Id { get; set; }
[JsonPropertyName("firstName")]
public string FirstName { get; set; }
[JsonPropertyName("nickname")]
public string Nickname { get; set; }
}
The attributes are the API's camelCase talking. The
Nickname property stays string —
reference types can be null already — and the notes record
that the key is missing from some elements, so a null
nickname in your data means “absent”, not “broken”.