⇄ Code & API Conversion

JSON to C# Class Converter

Convert JSON payloads into C# classes with nested types, arrays, DateTime, and Guid detection.

Updated

Custom root name DateTime detection Guid detection

Loading JSON → C#…

What this page sends

  • Your input: Processed in this tab and not sent to a server.
  • Share links: Share links put a Base64 copy of your input and output in the URL itself (after #d=). Anyone with the link can read it, so don't share a link that contains secrets.
  • Editor: On desktop screens the code editor (Monaco) is downloaded from cdn.jsdelivr.net; phones get a built-in lightweight editor instead.
  • Page load: Loading the page requests HTML, scripts and images from ByteKiln, fonts from Google Fonts, and sends Google Analytics page views, tool-usage events and catalog interactions (tool/category IDs, result status and whether a click followed search — never your input, output or search terms). Privacy & sharing

How the JSON to C# Class Converter Works

You've got a JSON response — from an API you're integrating, a webhook payload, a config file, or a database export — and you need a matching C# class to deserialize it into. For a handful of fields that's a two-minute job. For a real API response with nested objects, arrays, and a dozen properties, transcribing it by hand is slow and exactly the kind of task where you mistype a property name or pick the wrong numeric type without noticing. This tool reads one real JSON sample and generates the matching class or record tree in your browser, with no account and no server round-trip for the conversion. It's deliberately a sample-based inference tool, not a JSON Schema or OpenAPI generator — it looks at the actual values in front of it and makes a best-effort type guess for each one, the same way you would if you were doing it by hand, just faster and without typos. That also means it has the same blind spot you'd have: if a field is null in your sample, or an array's later items look different from its first, the tool can't see what it wasn't shown. Treat the output as a strong first draft — usually correct, always worth a quick read before it goes into a real codebase.

How to use it

  1. Paste a real JSON object or array into the input panel, or start from the bundled sample to see the tool in action.
  2. Set a root class name if you want something more specific than the default (Root for an object, RootItem for an array) — useful once you have several generated files in the same project.
  3. Pick Class or Record for the output style, and turn on Nullable types if you want reference-typed properties marked ? defensively, even where this particular sample wasn't null.
  4. Turn on JSON attributes if any of your keys have hyphens, dots, spaces, or a leading digit — the generator adds [JsonPropertyName] only where the PascalCase conversion would otherwise break round-tripping.
  5. Set a namespace if you want the output wrapped in one, then copy or download the result and review it — especially the "what this doesn't do" items below — before compiling it into your project.

What it supports

  • ✓Primitive type inference: string, int, long (for numbers beyond int range), double, and bool
  • ✓DateTime detection from full ISO 8601 datetime strings (a bare date like "2024-01-15" with no time part is left as string)
  • ✓Guid detection from UUID-format strings
  • ✓Nested JSON objects become their own nested class, named after the property
  • ✓Arrays become List<T>, with T inferred from the first element
  • ✓Custom root class name, or automatic Root / RootItem defaults
  • ✓Class ({ get; set; }) or record ({ get; init; }) output
  • ✓Optional nullable (?) annotations on reference-typed properties
  • ✓Optional [JsonPropertyName] attributes (System.Text.Json) for keys that can't become clean PascalCase identifiers
  • ✓Optional namespace wrapping using file-scoped namespace syntax (namespace X;)

What it doesn't do

  • –Doesn't read a JSON Schema or OpenAPI spec — it infers structure from exactly one sample, so a field that's optional in your API but present in this particular sample won't be marked nullable unless you turn that option on yourself
  • –For any array, root or nested, only the first element is inspected — a heterogeneous array won't be fully modeled
  • –Never infers decimal — numbers always come out as int, long, or double, even for money fields; change those by hand where precision matters
  • –Doesn't deduplicate identical nested shapes under different property names — billingAddress and shippingAddress with identical fields become two separate, duplicate classes
  • –[JsonPropertyName] is only added for hyphens, dots, spaces, or a leading digit — a snake_case key like first_name gets no attribute (see the FAQ below)
  • –Doesn't generate constructors, [Required]/validation attributes, or XML doc comments
  • –No enum inference — a field that's always one of a few repeated strings is still typed as string

Worked examples

A typical nested API response

JSON input
{
  "id": 1,
  "email": "jane@example.com",
  "createdAt": "2024-03-11T09:15:00Z",
  "isVerified": true,
  "profile": {
    "displayName": "Jane Smith",
    "bio": null
  },
  "roles": ["admin", "editor"]
}
C# output (defaults)
public class Root
{
    public int Id { get; set; }
    public string Email { get; set; }
    public DateTime CreatedAt { get; set; }
    public bool IsVerified { get; set; }
    public Profile Profile { get; set; }
    public List<string> Roles { get; set; }
}

public class Profile
{
    public string DisplayName { get; set; }
    public string? Bio { get; set; }
}

The nested "profile" object becomes its own Profile class. "bio" is string? because it was null in the sample — that's automatic, independent of the Nullable types toggle.

An array root, with Nullable types and a namespace on

JSON input
[
  { "sku": "AB-100", "price": 49.99, "inStock": true },
  { "sku": "AB-101", "price": 12.5, "inStock": false }
]
C# output (namespace: MyApp.Models, Nullable types on)
namespace MyApp.Models;

public class RootItem
{
    public string? Sku { get; set; }
    public double Price { get; set; }
    public bool InStock { get; set; }
}

Only the first array item was used — the second item happens to match here, but the tool never checked. "Sku" got a ? because Nullable types marks reference types defensively; "InStock" stayed plain bool because bool is a value type, which that toggle doesn't touch.

Irregular keys, with JSON attributes on

JSON input
{
  "user-id": 42,
  "first.name": "Alex",
  "2fa_enabled": true,
  "snake_case_key": "value"
}
C# output (JSON attributes on)
using System.Text.Json.Serialization;

public class Root
{
    [JsonPropertyName("user-id")]
    public int UserId { get; set; }
    [JsonPropertyName("first.name")]
    public string FirstName { get; set; }
    [JsonPropertyName("2fa_enabled")]
    public bool 2faEnabled { get; set; }
    public string SnakeCaseKey { get; set; }
}

Two real gotchas in this output: "2faEnabled" starts with a digit, which is invalid C# and won't compile — rename it by hand. And "snake_case_key" got no [JsonPropertyName] at all (underscores don't trigger the attribute), so it needs one added manually to round-trip correctly.

Common errors and what they mean

The input shows a browser error like "Unexpected token , in JSON at position 42"

The input isn't valid JSON — usually a trailing comma, single-quoted strings, or unquoted keys. This is the raw JSON.parse error message passed through as-is; run the input through the JSON Formatter tool first for a line/column-level explanation, then bring the cleaned-up JSON back here.

"Input must be a JSON object or array"

You pasted a bare primitive at the top level — a plain string, number, true/false, or null. The generator needs an object or array to infer a class from.

"Cannot infer class from empty array"

An empty [] has no element to infer a shape from. Paste at least one sample item, or the array as it appears with real data in it.

The generated code won't compile because of a property name like "2faEnabled"

A JSON key that starts with a digit (like "2fa_enabled") produces a C# identifier that also starts with a digit, which is invalid — the generator doesn't guard against this. Rename that one property by hand after generating.

Deserializing your original JSON back into the generated class loses a value or throws

Usually a snake_case or otherwise-renamed key that needed a [JsonPropertyName] attribute but didn't get one, because the generator only adds it for hyphens, dots, spaces, or a leading digit — not underscores. Add the attribute by hand, or configure a naming policy on your JsonSerializerOptions.

FAQ

Short answers for the things developers usually ask before trusting a tool.

What .NET types does the ByteKiln JSON to C# generator infer?

string, int, long (for whole numbers over 2,147,483,647), double, bool, DateTime (from full ISO 8601 datetime strings, not bare dates), and Guid (from UUID-format strings). Nested objects become their own classes and arrays become List<T> properties.

What happens when a JSON value is null?

A null value is always typed as string? — the generator has no way to infer the real type from a null sample, so it defaults to a nullable string rather than guessing. Replace the type by hand once you know what it should really be.

Does this work with Newtonsoft.Json (Json.NET), or only System.Text.Json?

Only System.Text.Json. The "JSON attributes" option emits [JsonPropertyName("...")] from System.Text.Json.Serialization. If your project uses Newtonsoft.Json, swap that for [JsonProperty("...")] from the Newtonsoft.Json namespace.

Does it add [JsonPropertyName] for snake_case keys like first_name?

No. The generator only adds a [JsonPropertyName] attribute when a key has a hyphen, a dot, a space, or a leading digit — the kinds of keys that can't cleanly become a PascalCase identifier. A snake_case key like first_name becomes FirstName with no attribute, so if you deserialize the original JSON with System.Text.Json you still need PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower (or add the attribute yourself) or the values won't map back.

What is the difference between generating a class and a record?

Class output uses { get; set; } — ordinary mutable properties. Record output uses { get; init; } — properties that can only be set during construction, matching how C# records are normally used for immutable data. Both are plain property bags; neither option adds a constructor.

Does it handle an array of objects, or only single objects?

Both, but for any array — the top-level input or a nested property — only the first element is inspected to infer the shape. If you paste an array where later items have fields the first item doesn't, those extra fields won't appear in the generated class.

Can I use the generated classes in production?

As a starting point, yes — review it first. The generator infers int/long/double for all numbers (it never infers decimal, so money fields need a manual fix), doesn't deduplicate identical nested shapes under different property names, and doesn't add validation attributes or constructors.

Is my JSON data sent anywhere?

No. The JSON is parsed and the classes generated in your browser; the payload is not sent to a ByteKiln server or logged by the tool. If you press Share, the link itself carries a readable copy of your input, so don't share links that contain secrets.

Related tools

Useful follow-ups when one conversion usually turns into three more.

Want the background and worked examples? There's a longer write-up.

Read the JSON → C# guide

Related guides