100% local — your data never leaves your browser

JSON to C# — Records System.Text.Json Reads

Turn a JSON sample into C# records or classes. Every renamed property carries a JsonPropertyName attribute, so the deserializer still finds its key.

Instant Private Zero cookies
Style

JSON input

C# output

What this tool does

A JSON sample becomes C# records, one per object.

{ "id": 1, "firstName": "Ada", "address": { "city": "Paris" } }
using System.Text.Json.Serialization;

public record Root(
    [property: JsonPropertyName("id")] long Id,
    [property: JsonPropertyName("firstName")] string FirstName,
    [property: JsonPropertyName("address")] Address Address
);

public record Address(
    [property: JsonPropertyName("city")] string City
);

Positional records need C# 9; the using appears only when an attribute does.

The attribute target matters

C# property names are PascalCase, so firstName becomes FirstName and content-type becomes ContentType. System.Text.Json matches names case-insensitively only if you ask it to, and never across a dash — so the key is pinned with an attribute.

The property: prefix is the part that is easy to get wrong. On a positional record, the parameter is not the property: an attribute without a target sits on the constructor parameter and the serializer never sees it. With the target it lands where it is read.

Types

  • Integers become long, decimals double — both wider than the 32-bit default, because a JSON number does not promise to fit in one.
  • null and unknown values become object?, which needs nullable reference types enabled (the default since .NET 6).
  • Arrays become List<T>; a mixed array becomes List<object>, except when it mixes integers and decimals, where List<double> covers both.
  • A scalar at the root is refused: C# has no global type alias, so 42 has nothing to become. An array of objects gives you the record of its element, which is what you bind to a List<Root>.

What a sample cannot say

No property is nullable beyond those object?, and none has a default: the document had a value for each. required and init are worth adding once you know which keys are truly mandatory — a sample can only tell you which ones happened to be there.

Private by design

Everything runs locally in your browser with JavaScript. Your data is never uploaded, which makes the tool safe for sensitive content, and it keeps working offline.

Frequently asked questions

Why `[property: JsonPropertyName(...)]` and not just `[JsonPropertyName(...)]`?
Because a positional record parameter is not a property yet. Without the `property:` target the attribute lands on the constructor parameter, where the serializer never looks — the compiler even warns about it. The target moves it onto the generated property, which is where `System.Text.Json` reads it.
I use Newtonsoft.Json. What do I change?
The attribute and its using: `[property: JsonProperty("content-type")]` with `Newtonsoft.Json`. Nothing else differs — the property names, the types and the record shape are the same. Newtonsoft also matches case-insensitively by default, so plain `camelCase` keys often work without an attribute at all.
Record or class?
The record is the default: value equality, `with` expressions, and a constructor that makes every field explicit at creation. The class option gives you settable properties instead, which is what an older codebase or a framework that builds objects by property assignment expects. The types inferred are identical.

Related converters